diff --git a/db/mork/build/moz.build b/db/mork/build/moz.build new file mode 100644 index 0000000000..f77162cef1 --- /dev/null +++ b/db/mork/build/moz.build @@ -0,0 +1,24 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'nsIMdbFactoryFactory.h', + 'nsMorkCID.h', +] + +SOURCES += [ + 'nsMorkFactory.cpp', +] + +if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']: + XPCOMBinaryComponent('mork') + USE_LIBS += [ + 'nspr', + 'xpcomglue_s', + 'xul', + ] +else: + Library('mork') + FINAL_LIBRARY = 'xul' diff --git a/db/mork/build/nsIMdbFactoryFactory.h b/db/mork/build/nsIMdbFactoryFactory.h new file mode 100644 index 0000000000..8b5294396f --- /dev/null +++ b/db/mork/build/nsIMdbFactoryFactory.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsIMdbFactoryFactory_h__ +#define nsIMdbFactoryFactory_h__ + +#include "nsISupports.h" +#include "nsIFactory.h" +#include "nsIComponentManager.h" + +class nsIMdbFactory; + +// 2794D0B7-E740-47a4-91C0-3E4FCB95B806 +#define NS_IMDBFACTORYFACTORY_IID \ +{ 0x2794d0b7, 0xe740, 0x47a4, { 0x91, 0xc0, 0x3e, 0x4f, 0xcb, 0x95, 0xb8, 0x6 } } + +// because Mork doesn't support XPCOM, we have to wrap the mdb factory interface +// with an interface that gives you an mdb factory. +class nsIMdbFactoryService : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORYFACTORY_IID) + NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory) = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactoryService, NS_IMDBFACTORYFACTORY_IID) + +#endif diff --git a/db/mork/build/nsMorkCID.h b/db/mork/build/nsMorkCID.h new file mode 100644 index 0000000000..79d7a60746 --- /dev/null +++ b/db/mork/build/nsMorkCID.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsMorkCID_h__ +#define nsMorkCID_h__ + +#include "nsISupports.h" +#include "nsIFactory.h" +#include "nsIComponentManager.h" + +#define NS_MORK_CONTRACTID \ + "@mozilla.org/db/mork;1" + +// 36d90300-27f5-11d3-8d74-00805f8a6617 +#define NS_MORK_CID \ +{ 0x36d90300, 0x27f5, 0x11d3, \ + { 0x8d, 0x74, 0x00, 0x80, 0x5f, 0x8a, 0x66, 0x17 } } + +#endif diff --git a/db/mork/build/nsMorkFactory.cpp b/db/mork/build/nsMorkFactory.cpp new file mode 100644 index 0000000000..6ca358293d --- /dev/null +++ b/db/mork/build/nsMorkFactory.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ModuleUtils.h" +#include "nsCOMPtr.h" +#include "nsMorkCID.h" +#include "nsIMdbFactoryFactory.h" +#include "mdb.h" + +class nsMorkFactoryService final : public nsIMdbFactoryService +{ +public: + nsMorkFactoryService() {}; + // nsISupports methods + NS_DECL_ISUPPORTS + + NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory) override; + +protected: + ~nsMorkFactoryService() {} + nsCOMPtr mMdbFactory; +}; + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkFactoryService) + +NS_DEFINE_NAMED_CID(NS_MORK_CID); + +const mozilla::Module::CIDEntry kMorkCIDs[] = { + { &kNS_MORK_CID, false, NULL, nsMorkFactoryServiceConstructor }, + { NULL } +}; + +const mozilla::Module::ContractIDEntry kMorkContracts[] = { + { NS_MORK_CONTRACTID, &kNS_MORK_CID }, + { NULL } +}; + +static const mozilla::Module kMorkModule = { + mozilla::Module::kVersion, + kMorkCIDs, + kMorkContracts +}; + +NSMODULE_DEFN(nsMorkModule) = &kMorkModule; + +NS_IMPL_ISUPPORTS(nsMorkFactoryService, nsIMdbFactoryService) + +NS_IMETHODIMP nsMorkFactoryService::GetMdbFactory(nsIMdbFactory **aFactory) +{ + if (!mMdbFactory) + mMdbFactory = MakeMdbFactory(); + NS_IF_ADDREF(*aFactory = mMdbFactory); + return *aFactory ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} diff --git a/db/mork/public/mdb.h b/db/mork/public/mdb.h new file mode 100644 index 0000000000..62ab1adc70 --- /dev/null +++ b/db/mork/public/mdb.h @@ -0,0 +1,2520 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Blake Ross (blake@blakeross.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MDB_ +#define _MDB_ 1 + +#include "nscore.h" +#include "nsISupports.h" +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { %%%%% begin scalar typedefs %%%%% +typedef unsigned char mdb_u1; // make sure this is one byte +typedef unsigned short mdb_u2; // make sure this is two bytes +typedef short mdb_i2; // make sure this is two bytes +typedef uint32_t mdb_u4; // make sure this is four bytes +typedef int32_t mdb_i4; // make sure this is four bytes +typedef PRWord mdb_ip; // make sure sizeof(mdb_ip) == sizeof(void*) + +typedef mdb_u1 mdb_bool; // unsigned byte with zero=false, nonzero=true + +/* canonical boolean constants provided only for code clarity: */ +#define mdbBool_kTrue ((mdb_bool) 1) /* actually any nonzero means true */ +#define mdbBool_kFalse ((mdb_bool) 0) /* only zero means false */ + +typedef mdb_u4 mdb_id; // unsigned object identity in a scope +typedef mdb_id mdb_rid; // unsigned row identity inside scope +typedef mdb_id mdb_tid; // unsigned table identity inside scope +typedef mdb_u4 mdb_token; // unsigned token for atomized string +typedef mdb_token mdb_scope; // token used to id scope for rows +typedef mdb_token mdb_kind; // token used to id kind for tables +typedef mdb_token mdb_column; // token used to id columns for rows +typedef mdb_token mdb_cscode; // token used to id charset names +typedef mdb_u4 mdb_seed; // unsigned collection change counter +typedef mdb_u4 mdb_count; // unsigned collection member count +typedef mdb_u4 mdb_size; // unsigned physical media size +typedef mdb_u4 mdb_fill; // unsigned logical content size +typedef mdb_u4 mdb_more; // more available bytes for larger buffer + +typedef mdb_u2 mork_uses; // 2-byte strong uses count +typedef mdb_u2 mork_refs; // 2-byte actual reference count + +#define mdbId_kNone ((mdb_id) -1) /* never a valid Mork object ID */ + +typedef mdb_u4 mdb_percent; // 0..100, with values >100 same as 100 + +typedef mdb_u1 mdb_priority; // 0..9, for a total of ten different values + +// sequence position is signed; negative is useful to mean "before first": +typedef mdb_i4 mdb_pos; // signed zero-based ordinal collection position + +#define mdbPos_kBeforeFirst ((mdb_pos) -1) /* any negative is before zero */ + +// order is also signed, so we can use three states for comparison order: +typedef mdb_i4 mdb_order; // neg:lessthan, zero:equalto, pos:greaterthan + +typedef mdb_order (* mdbAny_Order)(const void* inA, const void* inB, + const void* inClosure); + +// } %%%%% end scalar typedefs %%%%% + +// { %%%%% begin C structs %%%%% + +#ifndef mdbScopeStringSet_typedef +typedef struct mdbScopeStringSet mdbScopeStringSet; +#define mdbScopeStringSet_typedef 1 +#endif + +/*| mdbScopeStringSet: a set of null-terminated C strings that enumerate some +**| names of row scopes, so that row scopes intended for use by an application +**| can be declared by an app when trying to open or create a database file. +**| (We use strings and not tokens because we cannot know the tokens for any +**| particular db without having first opened the db.) The goal is to inform +**| a db runtime that scopes not appearing in this list can be given relatively +**| short shrift in runtime representation, with the expectation that other +**| scopes will not actually be used. However, a db should still be prepared +**| to handle accessing row scopes not in this list, rather than raising errors. +**| But it could be quite expensive to access a row scope not on the list. +**| Note a zero count for the string set means no such string set is being +**| specified, and that a db should handle all row scopes efficiently. +**| (It does NOT mean an app plans to use no content whatsoever.) +|*/ +#ifndef mdbScopeStringSet_struct +#define mdbScopeStringSet_struct 1 +struct mdbScopeStringSet { // vector of scopes for use in db opening policy + // when mScopeStringSet_Count is zero, this means no scope constraints + mdb_count mScopeStringSet_Count; // number of strings in vector below + const char** mScopeStringSet_Strings; // null-ended ascii scope strings +}; +#endif /*mdbScopeStringSet_struct*/ + +#ifndef mdbOpenPolicy_typedef +typedef struct mdbOpenPolicy mdbOpenPolicy; +#define mdbOpenPolicy_typedef 1 +#endif + +#ifndef mdbOpenPolicy_struct +#define mdbOpenPolicy_struct 1 +struct mdbOpenPolicy { // policies affecting db usage for ports and stores + mdbScopeStringSet mOpenPolicy_ScopePlan; // predeclare scope usage plan + mdb_bool mOpenPolicy_MaxLazy; // nonzero: do least work + mdb_bool mOpenPolicy_MinMemory; // nonzero: use least memory +}; +#endif /*mdbOpenPolicy_struct*/ + +#ifndef mdbTokenSet_typedef +typedef struct mdbTokenSet mdbTokenSet; +#define mdbTokenSet_typedef 1 +#endif + +#ifndef mdbTokenSet_struct +#define mdbTokenSet_struct 1 +struct mdbTokenSet { // array for a set of tokens, and actual slots used + mdb_count mTokenSet_Count; // number of token slots in the array + mdb_fill mTokenSet_Fill; // the subset of count slots actually used + mdb_more mTokenSet_More; // more tokens available for bigger array + mdb_token* mTokenSet_Tokens; // array of count mdb_token instances +}; +#endif /*mdbTokenSet_struct*/ + +#ifndef mdbUsagePolicy_typedef +typedef struct mdbUsagePolicy mdbUsagePolicy; +#define mdbUsagePolicy_typedef 1 +#endif + +/*| mdbUsagePolicy: another version of mdbOpenPolicy which uses tokens instead +**| of scope strings, because usage policies can be constructed for use with a +**| db that is already open, while an open policy must be constructed before a +**| db has yet been opened. +|*/ +#ifndef mdbUsagePolicy_struct +#define mdbUsagePolicy_struct 1 +struct mdbUsagePolicy { // policies affecting db usage for ports and stores + mdbTokenSet mUsagePolicy_ScopePlan; // current scope usage plan + mdb_bool mUsagePolicy_MaxLazy; // nonzero: do least work + mdb_bool mUsagePolicy_MinMemory; // nonzero: use least memory +}; +#endif /*mdbUsagePolicy_struct*/ + +#ifndef mdbOid_typedef +typedef struct mdbOid mdbOid; +#define mdbOid_typedef 1 +#endif + +#ifndef mdbOid_struct +#define mdbOid_struct 1 +struct mdbOid { // identity of some row or table inside a database + mdb_scope mOid_Scope; // scope token for an id's namespace + mdb_id mOid_Id; // identity of object inside scope namespace +}; +#endif /*mdbOid_struct*/ + +#ifndef mdbRange_typedef +typedef struct mdbRange mdbRange; +#define mdbRange_typedef 1 +#endif + +#ifndef mdbRange_struct +#define mdbRange_struct 1 +struct mdbRange { // range of row positions in a table + mdb_pos mRange_FirstPos; // position of first row + mdb_pos mRange_LastPos; // position of last row +}; +#endif /*mdbRange_struct*/ + +#ifndef mdbColumnSet_typedef +typedef struct mdbColumnSet mdbColumnSet; +#define mdbColumnSet_typedef 1 +#endif + +#ifndef mdbColumnSet_struct +#define mdbColumnSet_struct 1 +struct mdbColumnSet { // array of column tokens (just the same as mdbTokenSet) + mdb_count mColumnSet_Count; // number of columns + mdb_column* mColumnSet_Columns; // count mdb_column instances +}; +#endif /*mdbColumnSet_struct*/ + +#ifndef mdbYarn_typedef +typedef struct mdbYarn mdbYarn; +#define mdbYarn_typedef 1 +#endif + +#ifdef MDB_BEGIN_C_LINKAGE_define +#define MDB_BEGIN_C_LINKAGE_define 1 +#define MDB_BEGIN_C_LINKAGE extern "C" { +#define MDB_END_C_LINKAGE } +#endif /*MDB_BEGIN_C_LINKAGE_define*/ + +/*| mdbYarn_mGrow: an abstract API for growing the size of a mdbYarn +**| instance. With respect to a specific API that requires a caller +**| to supply a string (mdbYarn) that a callee fills with content +**| that might exceed the specified size, mdbYarn_mGrow is a caller- +**| supplied means of letting a callee attempt to increase the string +**| size to become large enough to receive all content available. +**| +**|| Grow(): a method for requesting that a yarn instance be made +**| larger in size. Note that such requests need not be honored, and +**| need not be honored in full if only partial size growth is desired. +**| (Note that no nsIMdbEnv instance is passed as argument, although one +**| might be needed in some circumstances. So if an nsIMdbEnv is needed, +**| a reference to one might be held inside a mdbYarn member slot.) +**| +**|| self: a yarn instance to be grown. Presumably this yarn is +**| the instance which holds the mYarn_Grow method pointer. Yarn +**| instancesshould only be passed to grow methods which they were +**| specifically designed to fit, as indicated by the mYarn_Grow slot. +**| +**|| inNewSize: the new desired value for slot mYarn_Size in self. +**| If mYarn_Size is already this big, then nothing should be done. +**| If inNewSize is larger than seems feasible or desirable to honor, +**| then any size restriction policy can be used to grow to some size +**| greater than mYarn_Size. (Grow() might even grow to a size +**| greater than inNewSize in order to make the increase in size seem +**| worthwhile, rather than growing in many smaller steps over time.) +|*/ +typedef void (* mdbYarn_mGrow)(mdbYarn* self, mdb_size inNewSize); +// mdbYarn_mGrow methods must be declared with C linkage in C++ + +/*| mdbYarn: a variable length "string" of arbitrary binary bytes, +**| whose length is mYarn_Fill, inside a buffer mYarn_Buf that has +**| at most mYarn_Size byte of physical space. +**| +**|| mYarn_Buf: a pointer to space containing content. This slot +**| might never be nil when mYarn_Size is nonzero, but checks for nil +**| are recommended anyway. +**| (Implementations of mdbYarn_mGrow methods should take care to +**| ensure the existence of a replacement before dropping old Bufs.) +**| Content in Buf can be anything in any format, but the mYarn_Form +**| implies the actual format by some caller-to-callee convention. +**| mYarn_Form==0 implies US-ASCII iso-8859-1 Latin1 string content. +**| +**|| mYarn_Size: the physical size of Buf in bytes. Note that if one +**| intends to terminate a string with a null byte, that it must not +**| be written at or after mYarn_Buf[mYarn_Size] because this is after +**| the last byte in the physical buffer space. Size can be zero, +**| which means the string has no content whatsoever; note that when +**| Size is zero, this is a suitable reason for Buf==nil as well. +**| +**|| mYarn_Fill: the logical content in Buf in bytes, where Fill must +**| never exceed mYarn_Size. Note that yarn strings might not have a +**| terminating null byte (since they might not even be C strings), but +**| when they do, such terminating nulls are considered part of content +**| and therefore Fill will count such null bytes. So an "empty" C +**| string will have Fill==1, because content includes one null byte. +**| Fill does not mean "length" when applied to C strings for this +**| reason. However, clients using yarns to hold C strings can infer +**| that length is equal to Fill-1 (but should take care to handle the +**| case where Fill==0). To be paranoid, one can always copy to a +**| destination with size exceeding Fill, and place a redundant null +**| byte in the Fill position when this simplifies matters. +**| +**|| mYarn_Form: a designation of content format within mYarn_Buf. +**| The semantics of this slot are the least well defined, since the +**| actual meaning is context dependent, to the extent that callers +**| and callees must agree on format encoding conventions when such +**| are not standardized in many computing contexts. However, in the +**| context of a specific mdb database, mYarn_Form is a token for an +**| atomized string in that database that typically names a preferred +**| mime type charset designation. If and when mdbYarn is used for +**| other purposes away from the mdb interface, folks can use another +**| convention system for encoding content formats. However, in all +**| contexts is it useful to maintain the convention that Form==0 +**| implies Buf contains US-ASCII iso-8859-1 Latin1 string content. +**| +**|| mYarn_Grow: either a mdbYarn_mGrow method, or else nil. When +**| a mdbYarn_mGrow method is provided, this method can be used to +**| request a yarn buf size increase. A caller who constructs the +**| original mdbYarn instance decides whether a grow method is necessary +**| or desirable, and uses only grow methods suitable for the buffering +**| nature of a specific mdbYarn instance. (For example, Buf might be a +**| staticly allocated string space which switches to something heap-based +**| when grown, and subsequent calls to grow the yarn must distinguish the +**| original static string from heap allocated space, etc.) Note that the +**| method stored in mYarn_Grow can change, and this might be a common way +**| to track memory managent changes in policy for mYarn_Buf. +|*/ +#ifndef mdbYarn_struct +#define mdbYarn_struct 1 +struct mdbYarn { // buffer with caller space allocation semantics + void* mYarn_Buf; // space for holding any binary content + mdb_fill mYarn_Fill; // logical content in Buf in bytes + mdb_size mYarn_Size; // physical size of Buf in bytes + mdb_more mYarn_More; // more available bytes if Buf is bigger + mdb_cscode mYarn_Form; // charset format encoding + mdbYarn_mGrow mYarn_Grow; // optional method to grow mYarn_Buf + + // Subclasses might add further slots after mYarn_Grow in order to + // maintain bookkeeping needs, such as state info about mYarn_Buf. +}; +#endif /*mdbYarn_struct*/ + +// } %%%%% end C structs %%%%% + +// { %%%%% begin class forward defines %%%%% +class nsIMdbEnv; +class nsIMdbObject; +class nsIMdbErrorHook; +class nsIMdbThumb; +class nsIMdbFactory; +class nsIMdbFile; +class nsIMdbPort; +class nsIMdbStore; +class nsIMdbCursor; +class nsIMdbPortTableCursor; +class nsIMdbCollection; +class nsIMdbTable; +class nsIMdbTableRowCursor; +class nsIMdbRow; +class nsIMdbRowCellCursor; +class nsIMdbBlob; +class nsIMdbCell; +class nsIMdbSorting; +// } %%%%% end class forward defines %%%%% + + +// { %%%%% begin C++ abstract class interfaces %%%%% + +/*| nsIMdbObject: base class for all message db class interfaces +**| +**|| factory: all nsIMdbObjects from the same code suite have the same factory +**| +**|| refcounting: both strong and weak references, to ensure strong refs are +**| acyclic, while weak refs can cause cycles. CloseMdbObject() is +**| called when (strong) use counts hit zero, but clients can call this close +**| method early for some reason, if absolutely necessary even though it will +**| thwart the other uses of the same object. Note that implementations must +**| cope with close methods being called arbitrary numbers of times. The COM +**| calls to AddRef() and release ref map directly to strong use ref calls, +**| but the total ref count for COM objects is the sum of weak & strong refs. +|*/ + +#define NS_IMDBOBJECT_IID_STR "5533ea4b-14c3-4bef-ac60-22f9e9a49084" + +#define NS_IMDBOBJECT_IID \ +{0x5533ea4b, 0x14c3, 0x4bef, \ +{ 0xac, 0x60, 0x22, 0xf9, 0xe9, 0xa4, 0x90, 0x84}} + +class nsIMdbObject : public nsISupports { // msg db base class +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBOBJECT_IID) +// { ===== begin nsIMdbObject methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) = 0; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin factory methods ----- + NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) = 0; + // } ----- end factory methods ----- + + // { ----- begin ref counting for well-behaved cyclic graphs ----- + NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs + mdb_count* outCount) = 0; + NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs + mdb_count* outCount) = 0; + + NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) = 0; + NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) = 0; + + NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) = 0; + NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) = 0; + + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) = 0; // called at strong refs zero + NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) = 0; + // } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbObject, NS_IMDBOBJECT_IID) + +/*| nsIMdbErrorHook: a base class for clients of this API to subclass, in order +**| to provide a callback installable in nsIMdbEnv for error notifications. If +**| apps that subclass nsIMdbErrorHook wish to maintain a reference to the env +**| that contains the hook, then this should be a weak ref to avoid cycles. +**| +**|| OnError: when nsIMdbEnv has an error condition that causes the total count +**| of errors to increase, then nsIMdbEnv should call OnError() to report the +**| error in some fashion when an instance of nsIMdbErrorHook is installed. The +**| variety of string flavors is currently due to the uncertainty here in the +**| nsIMdbBlob and nsIMdbCell interfaces. (Note that overloading by using the +**| same method name is not necessary here, and potentially less clear.) +|*/ +class nsIMdbErrorHook : public nsISupports{ // env callback handler to report errors +public: + +// { ===== begin error methods ===== + NS_IMETHOD OnErrorString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnErrorYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; +// } ===== end error methods ===== + +// { ===== begin warning methods ===== + NS_IMETHOD OnWarningString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnWarningYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; +// } ===== end warning methods ===== + +// { ===== begin abort hint methods ===== + NS_IMETHOD OnAbortHintString(nsIMdbEnv* ev, const char* inAscii) = 0; + NS_IMETHOD OnAbortHintYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0; +// } ===== end abort hint methods ===== +}; + +/*| nsIMdbHeap: abstract memory allocation interface. +**| +**|| Alloc: return a block at least inSize bytes in size with alignment +**| suitable for any native type (such as long integers). When no such +**| block can be allocated, failure is indicated by a null address in +**| addition to reporting an error in the environment. +**| +**|| Free: deallocate a block allocated or resized earlier by the same +**| heap instance. If the inBlock parameter is nil, the heap should do +**| nothing (and crashing is strongly discouraged). +|*/ +class nsIMdbHeap { // caller-supplied memory management interface +public: +// { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested byte size of new memory block + void** outBlock) = 0; // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block from Alloc or Resize() + void* ioBlock) = 0; // block to be destroyed/deallocated + + virtual size_t GetUsedSize() = 0; + + virtual ~nsIMdbHeap() {}; +// } ===== end nsIMdbHeap methods ===== +}; + +/*| nsIMdbCPlusHeap: Alloc() with global ::new(), Free() with global ::delete(). +**| Resize() is done by ::new() followed by ::delete(). +|*/ +class nsIMdbCPlusHeap { // caller-supplied memory management interface +public: +// { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock); // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock); + + NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev); + NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev); +// } ===== end nsIMdbHeap methods ===== +}; + +/*| nsIMdbThumb: +|*/ + + +#define NS_IMDBTHUMB_IID_STR "6d3ad7c1-a809-4e74-8577-49fa9a4562fa" + +#define NS_IMDBTHUMB_IID \ +{0x6d3ad7c1, 0xa809, 0x4e74, \ +{ 0x85, 0x77, 0x49, 0xfa, 0x9a, 0x45, 0x62, 0xfa}} + + +class nsIMdbThumb : public nsISupports { // closure for repeating incremental method +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTHUMB_IID) + +// { ===== begin nsIMdbThumb methods ===== + NS_IMETHOD GetProgress(nsIMdbEnv* ev, + mdb_count* outTotal, // total somethings to do in operation + mdb_count* outCurrent, // subportion of total completed so far + mdb_bool* outDone, // is operation finished? + mdb_bool* outBroken // is operation irreparably dead and broken? + ) = 0; + + NS_IMETHOD DoMore(nsIMdbEnv* ev, + mdb_count* outTotal, // total somethings to do in operation + mdb_count* outCurrent, // subportion of total completed so far + mdb_bool* outDone, // is operation finished? + mdb_bool* outBroken // is operation irreparably dead and broken? + ) = 0; + + NS_IMETHOD CancelAndBreakThumb( // cancel pending operation + nsIMdbEnv* ev) = 0; +// } ===== end nsIMdbThumb methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbThumb, NS_IMDBTHUMB_IID) + +/*| nsIMdbEnv: a context parameter used when calling most abstract db methods. +**| The main purpose of such an object is to permit a database implementation +**| to avoid the use of globals to share information between various parts of +**| the implementation behind the abstract db interface. An environment acts +**| like a session object for a given calling thread, and callers should use +**| at least one different nsIMdbEnv instance for each thread calling the API. +**| While the database implementation might not be threaded, it is highly +**| desirable that the db be thread-safe if calling threads use distinct +**| instances of nsIMdbEnv. Callers can stop at one nsIMdbEnv per thread, or they +**| might decide to make on nsIMdbEnv instance for every nsIMdbPort opened, so that +**| error information is segregated by database instance. Callers create +**| instances of nsIMdbEnv by calling the MakeEnv() method in nsIMdbFactory. +**| +**|| tracing: an environment might support some kind of tracing, and this +**| boolean attribute permits such activity to be enabled or disabled. +**| +**|| errors: when a call to the abstract db interface returns, a caller might +**| check the number of outstanding errors to see whether the operation did +**| actually succeed. Each nsIMdbEnv should have all its errors cleared by a +**| call to ClearErrors() before making each call to the abstract db API, +**| because outstanding errors might disable further database actions. (This +**| is not done inside the db interface, because the db cannot in general know +**| when a call originates from inside or outside -- only the app knows this.) +**| +**|| error hook: callers can install an instance of nsIMdbErrorHook to receive +**| error notifications whenever the error count increases. The hook can +**| be uninstalled by passing a null pointer. +**| +|*/ + +#define NS_IMDBENV_IID_STR "a765e46b-efb6-41e6-b75b-c5d6bd710594" + +#define NS_IMDBENV_IID \ +{0xa765e46b, 0xefb6, 0x41e6, \ +{ 0xb7, 0x5b, 0xc5, 0xd6, 0xbd, 0x71, 0x05, 0x94}} + +class nsIMdbEnv : public nsISupports { // db specific context parameter +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBENV_IID) +// { ===== begin nsIMdbEnv methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetErrorCount(mdb_count* outCount, + mdb_bool* outShouldAbort) = 0; + NS_IMETHOD GetWarningCount(mdb_count* outCount, + mdb_bool* outShouldAbort) = 0; + + NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) = 0; + NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) = 0; + + NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) = 0; + NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) = 0; + + NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) = 0; + NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) = 0; + + NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) = 0; + NS_IMETHOD SetErrorHook( + nsIMdbErrorHook* ioErrorHook) = 0; // becomes referenced + + NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) = 0; + NS_IMETHOD SetHeap( + nsIMdbHeap* ioHeap) = 0; // becomes referenced + // } ----- end attribute methods ----- + + NS_IMETHOD ClearErrors() = 0; // clear errors beore re-entering db API + NS_IMETHOD ClearWarnings() = 0; // clear warnings + NS_IMETHOD ClearErrorsAndWarnings() = 0; // clear both errors & warnings +// } ===== end nsIMdbEnv methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbEnv, NS_IMDBENV_IID) + +/*| nsIMdbFactory: the main entry points to the abstract db interface. A DLL +**| that supports this mdb interface need only have a single exported method +**| that will return an instance of nsIMdbFactory, so that further methods in +**| the suite can be accessed from objects returned by nsIMdbFactory methods. +**| +**|| mdbYarn: note all nsIMdbFactory subclasses must guarantee null +**| termination of all strings written into mdbYarn instances, as long as +**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must +**| be null terminated. This is more strict behavior than mdbYarn requires, +**| but it is part of the nsIMdbFactory interface. +**| +**|| envs: an environment instance is required as per-thread context for +**| most of the db method calls, so nsIMdbFactory creates such instances. +**| +**|| rows: callers must be able to create row instances that are independent +**| of storage space that is part of the db content graph. Many interfaces +**| for data exchange have strictly copy semantics, so that a row instance +**| has no specific identity inside the db content model, and the text in +**| cells are an independenty copy of unexposed content inside the db model. +**| Callers are expected to maintain one or more row instances as a buffer +**| for staging cell content copied into or out of a table inside the db. +**| Callers are urged to use an instance of nsIMdbRow created by the nsIMdbFactory +**| code suite, because reading and writing might be much more efficient than +**| when using a hand-rolled nsIMdbRow subclass with no relation to the suite. +**| +**|| ports: a port is a readonly interface to a specific database file. Most +**| of the methods to access a db file are suitable for a readonly interface, +**| so a port is the basic minimum for accessing content. This makes it +**| possible to read other external formats for import purposes, without +**| needing the code or competence necessary to write every such format. So +**| we can write generic import code just once, as long as every format can +**| show a face based on nsIMdbPort. (However, same suite import can be faster.) +**| Given a file name and the first 512 bytes of a file, a factory can say if +**| a port can be opened by this factory. Presumably an app maintains chains +**| of factories for different suites, and asks each in turn about opening a +**| a prospective file for reading (as a port) or writing (as a store). I'm +**| not ready to tackle issues of format fidelity and factory chain ordering. +**| +**|| stores: a store is a mutable interface to a specific database file, and +**| includes the port interface plus any methods particular to writing, which +**| are few in number. Presumably the set of files that can be opened as +**| stores is a subset of the set of files that can be opened as ports. A +**| new store can be created with CreateNewFileStore() by supplying a new +**| file name which does not yet exist (callers are always responsible for +**| destroying any existing files before calling this method). +|*/ + +#define NS_IMDBFACTORY_IID_STR "2b80395c-b91e-4990-b1a7-023e99ab14e9" + +#define NS_IMDBFACTORY_IID \ +{0xf04aa4ab, 0x1fe, 0x4115, \ +{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}} + + +class nsIMdbFactory : public nsISupports { // suite entry points +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORY_IID) +// { ===== begin nsIMdbFactory methods ===== + + // { ----- begin file methods ----- + NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, + mdb_bool inFrozen, nsIMdbFile** acqFile) = 0; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, + nsIMdbFile** acqFile) = 0; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + // } ----- end file methods ----- + + // { ----- begin env methods ----- + NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) = 0; // acquire new env + // ioHeap can be nil, causing a MakeHeap() style heap instance to be used + // } ----- end env methods ----- + + // { ----- begin heap methods ----- + NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) = 0; // acquire new heap + // } ----- end heap methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) = 0; // new row + // ioHeap can be nil, causing the heap associated with ev to be used + // } ----- end row methods ----- + + // { ----- begin port methods ----- + NS_IMETHOD CanOpenFilePort( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) = 0; // informal file format description + + NS_IMETHOD OpenFilePort( + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental port open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + + NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) = 0; // acquire new port object + // } ----- end port methods ----- + + // { ----- begin store methods ----- + NS_IMETHOD CanOpenFileStore( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) = 0; // informal file format description + + NS_IMETHOD OpenFileStore( // open an existing database + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental store open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + + NS_IMETHOD + ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) = 0; // acquire new db store object + + NS_IMETHOD CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) = 0; // acquire new db store object + // } ----- end store methods ----- + +// } ===== end nsIMdbFactory methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactory, NS_IMDBFACTORY_IID) + +extern "C" nsIMdbFactory* MakeMdbFactory(); + +/*| nsIMdbFile: abstract file interface resembling the original morkFile +**| abstract interface (which was in turn modeled on the file interface +**| from public domain IronDoc). The design of this file interface is +**| complicated by the fact that some DB's will not find this interface +**| adequate for all runtime requirements (even though this file API is +**| enough to implement text-based DB's like Mork). For this reason, +**| more methods have been added to let a DB library force the file to +**| become closed so the DB can reopen the file in some other manner. +**| Folks are encouraged to suggest ways to tune this interface to suit +**| DB's that cannot manage to pull their maneuvers even given this API. +**| +**|| Tell: get the current i/o position in file +**| +**|| Seek: change the current i/o position in file +**| +**|| Eof: return file's total length in bytes +**| +**|| Read: input inSize bytes into outBuf, returning actual transfer size +**| +**|| Get: read starting at specific file offset (e.g. Seek(); Read();) +**| +**|| Write: output inSize bytes from inBuf, returning actual transfer size +**| +**|| Put: write starting at specific file offset (e.g. Seek(); Write();) +**| +**|| Flush: if written bytes are buffered, push them to final destination +**| +**|| Path: get file path in some string representation. This is intended +**| either to support the display of file name in a user presentation, or +**| to support the closing and reopening of the file when the DB needs more +**| exotic file access than is presented by the nsIMdbFile interface. +**| +**|| Steal: tell this file to close any associated i/o stream in the file +**| system, because the file ioThief intends to reopen the file in order +**| to provide the MDB implementation with more exotic file access than is +**| offered by the nsIMdbFile alone. Presumably the thief knows enough +**| from Path() in order to know which file to reopen. If Steal() is +**| successful, this file should probably delegate all future calls to +**| the nsIMdbFile interface down to the thief files, so that even after +**| the file has been stolen, it can still be read, written, or forcibly +**| closed (by a call to CloseMdbObject()). +**| +**|| Thief: acquire and return thief passed to an earlier call to Steal(). +|*/ + +#define NS_IMDBFILE_IID_STR "f04aa4ab-1fe7-4115-a4a5-6819dff1103d" + +#define NS_IMDBFILE_IID \ +{0xf04aa4ab, 0x1fe, 0x4115, \ +{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}} + +class nsIMdbFile : public nsISupports { // minimal file interface +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFILE_IID) +// { ===== begin nsIMdbFile methods ===== + + // { ----- begin pos methods ----- + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const = 0; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) = 0; + NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) = 0; + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) = 0; + NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) = 0; + // } ----- end read methods ----- + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) = 0; + NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) = 0; + NS_IMETHOD Flush(nsIMdbEnv* ev) = 0; + // } ----- end attribute methods ----- + + // { ----- begin path methods ----- + NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) = 0; + // } ----- end path methods ----- + + // { ----- begin replacement methods ----- + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0; + NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) = 0; + // } ----- end replacement methods ----- + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) = 0; + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. + // This default implementation of BecomeTrunk() does nothing, and this + // is appropriate behavior for files which are not branches, and is + // also the right behavior for files returned from AcquireBud() which are + // in fact the original file that has been truncated down to zero length. + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) = 0; // acquired file for new version of content + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. + // } ----- end versioning methods ----- + +// } ===== end nsIMdbFile methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFile, NS_IMDBFILE_IID) + +/*| nsIMdbPort: a readonly interface to a specific database file. The mutable +**| nsIMdbStore interface is a subclass that includes writing behavior, but +**| most of the needed db methods appear in the readonly nsIMdbPort interface. +**| +**|| mdbYarn: note all nsIMdbPort and nsIMdbStore subclasses must guarantee null +**| termination of all strings written into mdbYarn instances, as long as +**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must +**| be null terminated. This is more strict behavior than mdbYarn requires, +**| but it is part of the nsIMdbPort and nsIMdbStore interface. +**| +**|| attributes: methods are provided to distinguish a readonly port from a +**| mutable store, and whether a mutable store actually has any dirty content. +**| +**|| filepath: the file path used to open the port from the nsIMdbFactory can be +**| queried and discovered by GetPortFilePath(), which includes format info. +**| +**|| export: a port can write itself in other formats, with perhaps a typical +**| emphasis on text interchange formats used by other systems. A port can be +**| queried to determine its preferred export interchange format, and a port +**| can be queried to see whether a specific export format is supported. And +**| actually exporting a port requires a new destination file name and format. +**| +**|| tokens: a port supports queries about atomized strings to map tokens to +**| strings or strings to token integers. (All atomized strings must be in +**| US-ASCII iso-8859-1 Latin1 charset encoding.) When a port is actually a +**| mutable store and a string has not yet been atomized, then StringToToken() +**| will actually do so and modify the store. The QueryToken() method will not +**| atomize a string if it has not already been atomized yet, even in stores. +**| +**|| tables: other than string tokens, all port content is presented through +**| tables, which are ordered collections of rows. Tables are identified by +**| row scope and table kind, which might or might not be unique in a port, +**| depending on app convention. When tables are effectively unique, then +**| queries for specific scope and kind pairs will find those tables. To see +**| all tables that match specific row scope and table kind patterns, even in +**| the presence of duplicates, every port supports a GetPortTableCursor() +**| method that returns an iterator over all matching tables. Table kind is +**| considered scoped inside row scope, so passing a zero for table kind will +**| find all table kinds for some nonzero row scope. Passing a zero for row +**| scope will iterate over all tables in the port, in some undefined order. +**| (A new table can be added to a port using nsIMdbStore::NewTable(), even when +**| the requested scope and kind combination is already used by other tables.) +**| +**|| memory: callers can request that a database use less memory footprint in +**| several flavors, from an inconsequential idle flavor to a rather drastic +**| panic flavor. Callers might perform an idle purge very frequently if desired +**| with very little cost, since only normally scheduled memory management will +**| be conducted, such as freeing resources for objects scheduled to be dropped. +**| Callers should perform session memory purges infrequently because they might +**| involve costly scanning of data structures to removed cached content, and +**| session purges are recommended only when a caller experiences memory crunch. +**| Callers should only rarely perform a panic purge, in response to dire memory +**| straits, since this is likely to make db operations much more expensive +**| than they would be otherwise. A panic purge asks a database to free as much +**| memory as possible while staying effective and operational, because a caller +**| thinks application failure might otherwise occur. (Apps might better close +**| an open db, so panic purges only make sense when a db is urgently needed.) +|*/ +class nsIMdbPort : public nsISupports { +public: + +// { ===== begin nsIMdbPort methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) = 0; + + NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev, + mdbUsagePolicy* ioUsagePolicy) = 0; + + NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev, + const mdbUsagePolicy* inUsagePolicy) = 0; + // } ----- end attribute methods ----- + + // { ----- begin memory policy methods ----- + NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed + + NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease + nsIMdbEnv* ev, // context + mdb_size inDesiredBytesFreed, // approximate number of bytes wanted + mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed + + NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed + // } ----- end memory policy methods ----- + + // { ----- begin filepath methods ----- + NS_IMETHOD GetPortFilePath( + nsIMdbEnv* ev, // context + mdbYarn* outFilePath, // name of file holding port content + mdbYarn* outFormatVersion) = 0; // file format description + + NS_IMETHOD GetPortFile( + nsIMdbEnv* ev, // context + nsIMdbFile** acqFile) = 0; // acquire file used by port or store + // } ----- end filepath methods ----- + + // { ----- begin export methods ----- + NS_IMETHOD BestExportFormat( // determine preferred export format + nsIMdbEnv* ev, // context + mdbYarn* outFormatVersion) = 0; // file format description + + // some tentative suggested import/export formats + // "ns:msg:db:port:format:ldif:ns4.0:passthrough" // necessary + // "ns:msg:db:port:format:ldif:ns4.5:utf8" // necessary + // "ns:msg:db:port:format:ldif:ns4.5:tabbed" + // "ns:msg:db:port:format:ldif:ns4.5:binary" // necessary + // "ns:msg:db:port:format:html:ns3.0:addressbook" // necessary + // "ns:msg:db:port:format:html:display:verbose" + // "ns:msg:db:port:format:html:display:concise" + // "ns:msg:db:port:format:mork:zany:verbose" // necessary + // "ns:msg:db:port:format:mork:zany:atomized" // necessary + // "ns:msg:db:port:format:rdf:xml" + // "ns:msg:db:port:format:xml:mork" + // "ns:msg:db:port:format:xml:display:verbose" + // "ns:msg:db:port:format:xml:display:concise" + // "ns:msg:db:port:format:xml:print:verbose" // recommended + // "ns:msg:db:port:format:xml:print:concise" + + NS_IMETHOD + CanExportToFormat( // can export content in given specific format? + nsIMdbEnv* ev, // context + const char* inFormatVersion, // file format description + mdb_bool* outCanExport) = 0; // whether ExportSource() might succeed + + NS_IMETHOD ExportToFormat( // export content in given specific format + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to receive exported content + nsIMdbFile* ioFile, // destination abstract file interface + const char* inFormatVersion, // file format description + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental export + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the export will be finished. + + // } ----- end export methods ----- + + // { ----- begin token methods ----- + NS_IMETHOD TokenToString( // return a string name for an integer token + nsIMdbEnv* ev, // context + mdb_token inToken, // token for inTokenName inside this port + mdbYarn* outTokenName) = 0; // the type of table to access + + NS_IMETHOD StringToToken( // return an integer token for scope name + nsIMdbEnv* ev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) = 0; // token for inTokenName inside this port + + // String token zero is never used and never supported. If the port + // is a mutable store, then StringToToken() to create a new + // association of inTokenName with a new integer token if possible. + // But a readonly port will return zero for an unknown scope name. + + NS_IMETHOD QueryToken( // like StringToToken(), but without adding + nsIMdbEnv* ev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) = 0; // token for inTokenName inside this port + + // QueryToken() will return a string token if one already exists, + // but unlike StringToToken(), will not assign a new token if not + // already in use. + + // } ----- end token methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD HasRow( // contains a row with the specified oid? + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_bool* outHasRow) = 0; // whether GetRow() might succeed + + NS_IMETHOD GetRowRefCount( // get number of tables that contain a row + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_count* outRefCount) = 0; // number of tables containing inRowKey + + NS_IMETHOD GetRow( // access one row with specific oid + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + nsIMdbRow** acqRow) = 0; // acquire specific row (or null) + + // NS_IMETHOD + // GetPortRowCursor( // get cursor for all rows in specific scope + // nsIMdbEnv* ev, // context + // mdb_scope inRowScope, // row scope for row ids + // nsIMdbPortRowCursor** acqCursor) = 0; // all such rows in the port + + NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell + mdb_scope inRowScope, // row scope for row ids + mdb_column inColumn, // the column to search (and maintain an index) + const mdbYarn* inTargetCellValue, // cell value for which to search + mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match) + nsIMdbRow** acqRow) = 0; // acquire matching row (or nil for no match) + // can be null if you only want the oid + // FindRow() searches for one row that has a cell in column inColumn with + // a contained value with the same form (i.e. charset) and is byte-wise + // identical to the blob described by yarn inTargetCellValue. Both content + // and form of the yarn must be an exact match to find a matching row. + // + // (In other words, both a yarn's blob bytes and form are significant. The + // form is not expected to vary in columns used for identity anyway. This + // is intended to make the cost of FindRow() cheaper for MDB implementors, + // since any cell value atomization performed internally must necessarily + // make yarn form significant in order to avoid data loss in atomization.) + // + // FindRow() can lazily create an index on attribute inColumn for all rows + // with that attribute in row space scope inRowScope, so that subsequent + // calls to FindRow() will perform faster. Such an index might or might + // not be persistent (but this seems desirable if it is cheap to do so). + // Note that lazy index creation in readonly DBs is not very feasible. + // + // This FindRow() interface assumes that attribute inColumn is effectively + // an alternative means of unique identification for a row in a rowspace, + // so correct behavior is only guaranteed when no duplicates for this col + // appear in the given set of rows. (If more than one row has the same cell + // value in this column, no more than one will be found; and cutting one of + // two duplicate rows can cause the index to assume no other such row lives + // in the row space, so future calls return nil for negative search results + // even though some duplicate row might still live within the rowspace.) + // + // In other words, the FindRow() implementation is allowed to assume simple + // hash tables mapping unqiue column keys to associated row values will be + // sufficient, where any duplication is not recorded because only one copy + // of a given key need be remembered. Implementors are not required to sort + // all rows by the specified column. + // } ----- end row methods ----- + + // { ----- begin table methods ----- + NS_IMETHOD HasTable( // supports a table with the specified oid? + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical table oid + mdb_bool* outHasTable) = 0; // whether GetTable() might succeed + + NS_IMETHOD GetTable( // access one table with specific oid + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical table oid + nsIMdbTable** acqTable) = 0; // acquire specific table (or null) + + NS_IMETHOD HasTableKind( // supports a table of the specified type? + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // rid scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outSupportsTable) = 0; // whether GetTableKind() might succeed + + // row scopes to be supported include the following suggestions: + // "ns:msg:db:row:scope:address:cards:all" + // "ns:msg:db:row:scope:mail:messages:all" + // "ns:msg:db:row:scope:news:articles:all" + + // table kinds to be supported include the following suggestions: + // "ns:msg:db:table:kind:address:cards:main" + // "ns:msg:db:table:kind:address:lists:all" + // "ns:msg:db:table:kind:address:list" + // "ns:msg:db:table:kind:news:threads:all" + // "ns:msg:db:table:kind:news:thread" + // "ns:msg:db:table:kind:mail:threads:all" + // "ns:msg:db:table:kind:mail:thread" + + NS_IMETHOD GetTableKind( // access one (random) table of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outMustBeUnique, // whether port can hold only one of these + nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows + + NS_IMETHOD + GetPortTableCursor( // get cursor for all tables of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + nsIMdbPortTableCursor** acqCursor) = 0; // all such tables in the port + // } ----- end table methods ----- + + + // { ----- begin commit methods ----- + + NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste? + nsIMdbEnv* ev, // context + mdb_percent inPercentWaste, // 0..100 percent file size waste threshold + mdb_percent* outActualWaste, // 0..100 percent of file actually wasted + mdb_bool* outShould) = 0; // true when about inPercentWaste% is wasted + // ShouldCompress() returns true if the store can determine that the file + // will shrink by an estimated percentage of inPercentWaste% (or more) if + // CompressCommit() is called, because that percentage of the file seems + // to be recoverable free space. The granularity is only in terms of + // percentage points, and any value over 100 is considered equal to 100. + // + // If a store only has an approximate idea how much space might be saved + // during a compress, then a best guess should be made. For example, the + // Mork implementation might keep track of how much file space began with + // text content before the first updating transaction, and then consider + // all content following the start of the first transaction as potentially + // wasted space if it is all updates and not just new content. (This is + // a safe assumption in the sense that behavior will stabilize on a low + // estimate of wastage after a commit removes all transaction updates.) + // + // Some db formats might attempt to keep a very accurate reckoning of free + // space size, so a very accurate determination can be made. But other db + // formats might have difficulty determining size of free space, and might + // require some lengthy calculation to answer. This is the reason for + // passing in the percentage threshold of interest, so that such lengthy + // computations can terminate early as soon as at least inPercentWaste is + // found, so that the entire file need not be groveled when unnecessary. + // However, we hope implementations will always favor fast but imprecise + // heuristic answers instead of extremely slow but very precise answers. + // + // If the outActualWaste parameter is non-nil, it will be used to return + // the actual estimated space wasted as a percentage of file size. (This + // parameter is provided so callers need not call repeatedly with altered + // inPercentWaste values to isolate the actual wastage figure.) Note the + // actual wastage figure returned can exactly equal inPercentWaste even + // when this grossly underestimates the real figure involved, if the db + // finds it very expensive to determine the extent of wastage after it is + // known to at least exceed inPercentWaste. Note we expect that whenever + // outShould returns true, that outActualWaste returns >= inPercentWaste. + // + // The effect of different inPercentWaste values is not very uniform over + // the permitted range. For example, 50 represents 50% wastage, or a file + // that is about double what it should be ideally. But 99 represents 99% + // wastage, or a file that is about ninety-nine times as big as it should + // be ideally. In the smaller direction, 25 represents 25% wastage, or + // a file that is only 33% larger than it should be ideally. + // + // Callers can determine what policy they want to use for considering when + // a file holds too much wasted space, and express this as a percentage + // of total file size to pass as in the inPercentWaste parameter. A zero + // likely returns always trivially true, and 100 always trivially false. + // The great majority of callers are expected to use values from 25 to 75, + // since most plausible thresholds for compressing might fall between the + // extremes of 133% of ideal size and 400% of ideal size. (Presumably the + // larger a file gets, the more important the percentage waste involved, so + // a sliding scale for compress thresholds might use smaller numbers for + // much bigger file sizes.) + + // } ----- end commit methods ----- + +// } ===== end nsIMdbPort methods ===== +}; + +/*| nsIMdbStore: a mutable interface to a specific database file. +**| +**|| tables: one can force a new table to exist in a store with NewTable() +**| and nonzero values for both row scope and table kind. (If one wishes only +**| one table of a certain kind, then one might look for it first using the +**| GetTableKind() method). One can pass inMustBeUnique to force future +**| users of this store to be unable to create other tables with the same pair +**| of scope and kind attributes. When inMustBeUnique is true, and the table +**| with the given scope and kind pair already exists, then the existing one +**| is returned instead of making a new table. Similarly, if one passes false +**| for inMustBeUnique, but the table kind has already been marked unique by a +**| previous user of the store, then the existing unique table is returned. +**| +**|| import: all or some of another port's content can be imported by calling +**| AddPortContent() with a row scope identifying the extent of content to +**| be imported. A zero row scope will import everything. A nonzero row +**| scope will only import tables with a matching row scope. Note that one +**| must somehow find a way to negotiate possible conflicts between existing +**| row content and imported row content, and this involves a specific kind of +**| definition for row identity involving either row IDs or unique attributes, +**| or some combination of these two. At the moment I am just going to wave +**| my hands, and say the default behavior is to assign all new row identities +**| to all imported content, which will result in no merging of content; this +**| must change later because it is unacceptable in some contexts. +**| +**|| commits: to manage modifications in a mutable store, very few methods are +**| really needed to indicate global policy choices that are independent of +**| the actual modifications that happen in objects at the level of tables, +**| rows, and cells, etc. The most important policy to specify is which sets +**| of changes are considered associated in a manner such that they should be +**| applied together atomically to a given store. We call each such group of +**| changes a transaction. We handle three different grades of transaction, +**| but they differ only in semantic significance to the application, and are +**| not intended to nest. (If small transactions were nested inside large +**| transactions, that would imply that a single large transaction must be +**| atomic over all the contained small transactions; but actually we intend +**| smalls transaction never be undone once commited due to, say, aborting a +**| transaction of greater significance.) The small, large, and session level +**| commits have equal granularity, and differ only in risk of loss from the +**| perspective of an application. Small commits characterize changes that +**| can be lost with relatively small risk, so small transactions can delay +**| until later if they are expensive or impractical to commit. Large commits +**| involve changes that would probably inconvenience users if lost, so the +**| need to pay costs of writing is rather greater than with small commits. +**| Session commits are last ditch attempts to save outstanding changes before +**| stopping the use of a particular database, so there will be no later point +**| in time to save changes that have been delayed due to possible high cost. +**| If large commits are never delayed, then a session commit has about the +**| same performance effect as another large commit; but if small and large +**| commits are always delayed, then a session commit is likely to be rather +**| expensive as a runtime cost compared to any earlier database usage. +**| +**|| aborts: the only way to abort changes to a store is by closing the store. +**| So there is no specific method for causing any abort. Stores must discard +**| all changes made that are uncommited when a store is closed. This design +**| choice makes the implementations of tables, rows, and cells much less +**| complex because they need not maintain a record of undobable changes. When +**| a store is closed, presumably this precipitates the closure of all tables, +**| rows, and cells in the store as well. So an application can revert the +**| state of a store in the user interface by quietly closing and reopening a +**| store, because this will discard uncommited changes and show old content. +**| This implies an app that closes a store will need to send a "scramble" +**| event notification to any views that depend on old discarded content. +|*/ + +#define NS_IMDBSTORE_IID_STR "74d6218d-44b0-43b5-9ebe-69a17dfb562c" +#define NS_IMDBSTORE_IID \ +{0x74d6218d, 0x44b0, 0x43b5, \ +{0x9e, 0xbe, 0x69, 0xa1, 0x7d, 0xfb, 0x56, 0x2c}} + +class nsIMdbStore : public nsIMdbPort { +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBSTORE_IID) + +// { ===== begin nsIMdbStore methods ===== + + // { ----- begin table methods ----- + NS_IMETHOD NewTable( // make one new table of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows + + NS_IMETHOD NewTableWithOid( // make one new table of specific type + nsIMdbEnv* ev, // context + const mdbOid* inOid, // caller assigned oid + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows + // } ----- end table methods ----- + + // { ----- begin row scope methods ----- + NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified + + NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified + + NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified + // } ----- end row scope methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid + const mdbOid* inOid, // caller assigned oid + nsIMdbRow** acqRow) = 0; // create new row + + NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid + mdb_scope inRowScope, // row scope for row ids + nsIMdbRow** acqRow) = 0; // create new row + // Note this row must be added to some table or cell child before the + // store is closed in order to make this row persist across sesssions. + + // } ----- end row methods ----- + + // { ----- begin inport/export methods ----- + NS_IMETHOD ImportContent( // import content from port + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // scope for rows (or zero for all?) + nsIMdbPort* ioPort, // the port with content to add to store + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the import will be finished. + + NS_IMETHOD ImportFile( // import content from port + nsIMdbEnv* ev, // context + nsIMdbFile* ioFile, // the file with content to add to store + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the import will be finished. + // } ----- end inport/export methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD + ShareAtomColumnsHint( // advise re shared column content atomizing + nsIMdbEnv* ev, // context + mdb_scope inScopeHint, // zero, or suggested shared namespace + const mdbColumnSet* inColumnSet) = 0; // cols desired tokenized together + + NS_IMETHOD + AvoidAtomColumnsHint( // advise column with poor atomizing prospects + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = 0; // cols with poor atomizing prospects + // } ----- end hinting methods ----- + + // { ----- begin commit methods ----- + NS_IMETHOD LargeCommit( // save important changes if at all possible + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + NS_IMETHOD SessionCommit( // save all changes if large commits delayed + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + NS_IMETHOD + CompressCommit( // commit and make db physically smaller if possible + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + // } ----- end commit methods ----- + +// } ===== end nsIMdbStore methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbStore, NS_IMDBSTORE_IID) + +/*| nsIMdbCursor: base cursor class for iterating row cells and table rows +**| +**|| count: the number of elements in the collection (table or row) +**| +**|| seed: the change count in the underlying collection, which is synced +**| with the collection when the iteration position is set, and henceforth +**| acts to show whether the iter has lost collection synchronization, in +**| case it matters to clients whether any change happens during iteration. +**| +**|| pos: the position of the current element in the collection. Negative +**| means a position logically before the first element. A positive value +**| equal to count (or larger) implies a position after the last element. +**| To iterate over all elements, set the position to negative, so subsequent +**| calls to any 'next' method will access the first collection element. +**| +**|| doFailOnSeedOutOfSync: whether a cursor should return an error if the +**| cursor's snapshot of a table's seed becomes stale with respect the table's +**| current seed value (which implies the iteration is less than total) in +**| between to cursor calls that actually access collection content. By +**| default, a cursor should assume this attribute is false until specified, +**| so that iterations quietly try to re-sync when they lose coherence. +|*/ + +#define NS_IMDBCURSOR_IID_STR "a0c37337-6ebc-474c-90db-e65ea0b850aa" + +#define NS_IMDBCURSOR_IID \ +{0xa0c37337, 0x6ebc, 0x474c, \ +{0x90, 0xdb, 0xe6, 0x5e, 0xa0, 0xb8, 0x50, 0xaa}} + +class nsIMdbCursor : public nsISupports { // collection iterator +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBCURSOR_IID) +// { ===== begin nsIMdbCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) = 0; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) = 0; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) = 0; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) = 0; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) = 0; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) = 0; + // } ----- end attribute methods ----- + +// } ===== end nsIMdbCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCursor, NS_IMDBCURSOR_IID) + +#define NS_IMDBPORTTABLECURSOR_IID_STR = "f181a41e-933d-49b3-af93-20d3634b8b78" + +#define NS_IMDBPORTTABLECURSOR_IID \ +{0xf181a41e, 0x933d, 0x49b3, \ +{0xaf, 0x93, 0x20, 0xd3, 0x63, 0x4b, 0x8b, 0x78}} + +/*| nsIMdbPortTableCursor: cursor class for iterating port tables +**| +**|| port: the cursor is associated with a specific port, which can be +**| set to a different port (which resets the position to -1 so the +**| next table acquired is the first in the port. +**| +|*/ +class nsIMdbPortTableCursor : public nsISupports { // table collection iterator +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBPORTTABLECURSOR_IID) +// { ===== begin nsIMdbPortTableCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) = 0; // sets pos to -1 + NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) = 0; + + NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1 + mdb_scope inRowScope) = 0; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; + // setting row scope to zero iterates over all row scopes in port + + NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1 + mdb_kind inTableKind) = 0; + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0; + // setting table kind to zero iterates over all table kinds in row scope + // } ----- end attribute methods ----- + + // { ----- begin table iteration methods ----- + NS_IMETHOD NextTable( // get table at next position in the db + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) = 0; // the next table in the iteration + // } ----- end table iteration methods ----- + +// } ===== end nsIMdbPortTableCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbPortTableCursor, + NS_IMDBPORTTABLECURSOR_IID) + +/*| nsIMdbCollection: an object that collects a set of other objects as members. +**| The main purpose of this base class is to unify the perceived semantics +**| of tables and rows where their collection behavior is similar. This helps +**| isolate the mechanics of collection behavior from the other semantics that +**| are more characteristic of rows and tables. +**| +**|| count: the number of objects in a collection is the member count. (Some +**| collection interfaces call this attribute the 'size', but that can be a +**| little ambiguous, and counting actual members is harder to confuse.) +**| +**|| seed: the seed of a collection is a counter for changes in membership in +**| a specific collection. This seed should change when members are added to +**| or removed from a collection, but not when a member changes internal state. +**| The seed should also change whenever the internal collection of members has +**| a complex state change that reorders member positions (say by sorting) that +**| would affect the nature of an iteration over that collection of members. +**| The purpose of a seed is to inform any outstanding collection cursors that +**| they might be stale, without incurring the cost of broadcasting an event +**| notification to such cursors, which would need more data structure support. +**| Presumably a cursor in a particular mdb code suite has much more direct +**| access to a collection seed member slot that this abstract COM interface, +**| so this information is intended more for clients outside mdb that want to +**| make inferences similar to those made by the collection cursors. The seed +**| value as an integer magnitude is not very important, and callers should not +**| assume meaningful information can be derived from an integer value beyond +**| whether it is equal or different from a previous inspection. A seed uses +**| integers of many bits in order to make the odds of wrapping and becoming +**| equal to an earlier seed value have probability that is vanishingly small. +**| +**|| port: every collection is associated with a specific database instance. +**| +**|| cursor: a subclass of nsIMdbCursor suitable for this specific collection +**| subclass. The ability to GetCursor() from the base nsIMdbCollection class +**| is not really as useful as getting a more specifically typed cursor more +**| directly from the base class without any casting involved. So including +**| this method here is more for conceptual illustration. +**| +**|| oid: every collection has an identity that persists from session to +**| session. Implementations are probably able to distinguish row IDs from +**| table IDs, but we don't specify anything official in this regard. A +**| collection has the same identity for the lifetime of the collection, +**| unless identity is swapped with another collection by means of a call to +**| BecomeContent(), which is considered a way to swap a new representation +**| for an old well-known object. (Even so, only content appears to change, +**| while the identity seems to stay the same.) +**| +**|| become: developers can effectively cause two objects to swap identities, +**| in order to effect a complete swap between what persistent content is +**| represented by two oids. The caller should consider this a content swap, +**| and not identity wap, because identities will seem to stay the same while +**| only content changes. However, implementations will likely do this +**| internally by swapping identities. Callers must swap content only +**| between objects of similar type, such as a row with another row, and a +**| table with another table, because implementations need not support +**| cross-object swapping because it might break object name spaces. +**| +**|| dropping: when a caller expects a row or table will no longer be used, the +**| caller can tell the collection to 'drop activity', which means the runtime +**| object can have its internal representation purged to save memory or any +**| other resource that is being consumed by the collection's representation. +**| This has no effect on the collection's persistent content or semantics, +**| and is only considered a runtime effect. After a collection drops +**| activity, the object should still be as usable as before (because it has +**| NOT been closed), but further usage can be expensive to re-instate because +**| it might involve reallocating space and/or re-reading disk space. But +**| since this future usage is not expected, the caller does not expect to +**| pay the extra expense. An implementation can choose to implement +**| 'dropping activity' in different ways, or even not at all if this +**| operation is not really feasible. Callers cannot ask objects whether they +**| are 'dropped' or not, so this should be transparent. (Note that +**| implementors might fear callers do not really know whether future +**| usage will occur, and therefore might delay the act of dropping until +**| the near future, until seeing whether the object is used again +**| immediately elsewhere. Such use soon after the drop request might cause +**| the drop to be cancelled.) +|*/ +class nsIMdbCollection : public nsISupports { // sequence of objects +public: + +// { ===== begin nsIMdbCollection methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) = 0; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) = 0; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) = 0; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) = 0; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) = 0; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) = 0; + // } ----- end activity dropping methods ----- + +// } ===== end nsIMdbCollection methods ===== +}; + +/*| nsIMdbTable: an ordered collection of rows +**| +**|| row scope: an integer token for an atomized string in this database +**| that names a space for row IDs. This attribute of a table is intended +**| as guidance metainformation that helps with searching a database for +**| tables that operate on collections of rows of the specific type. By +**| convention, a table with a specific row scope is expected to focus on +**| containing rows that belong to that scope, however exceptions are easily +**| allowed because all rows in a table are known by both row ID and scope. +**| (A table with zero row scope is never allowed because this would make it +**| ambiguous to use a zero row scope when iterating over tables in a port to +**| indicate that all row scopes should be seen by a cursor.) +**| +**|| table kind: an integer token for an atomized string in this database +**| that names a kind of table as a subset of the associated row scope. This +**| attribute is intended as guidance metainformation to clarify the role of +**| this table with respect to other tables in the same row scope, and this +**| also helps search for such tables in a database. By convention, a table +**| with a specific table kind has a consistent role for containing rows with +**| respect to other collections of such rows in the same row scope. Also by +**| convention, at least one table in a row scope has a table kind purporting +**| to contain ALL the rows that belong in that row scope, so that at least +**| one table exists that allows all rows in a scope to be interated over. +**| (A table with zero table kind is never allowed because this would make it +**| ambiguous to use a zero table kind when iterating over tables in a port to +**| indicate that all table kinds in a row scope should be seen by a cursor.) +**| +**|| port: every table is considered part of some port that contains the +**| table, so that closing the containing port will cause the table to be +**| indirectly closed as well. We make it easy to get the containing port for +**| a table, because the port supports important semantic interfaces that will +**| affect how content in table is presented; the most important port context +**| that affects a table is specified by the set of token to string mappings +**| that affect all tokens used throughout the database, and which drive the +**| meanings of row scope, table kind, cell columns, etc. +**| +**|| cursor: a cursor that iterates over the rows in this table, where rows +**| have zero-based index positions from zero to count-1. Making a cursor +**| with negative position will next iterate over the first row in the table. +**| +**|| position: given any position from zero to count-1, a table will return +**| the row ID and row scope for the row at that position. (One can use the +**| GetRowAllCells() method to read that row, or else use a row cursor to both +**| get the row at some position and read its content at the same time.) The +**| position depends on whether a table is sorted, and upon the actual sort. +**| Note that moving a row's position is only possible in unsorted tables. +**| +**|| row set: every table contains a collection of rows, where a member row is +**| referenced by the table using the row ID and row scope for the row. No +**| single table owns a given row instance, because rows are effectively ref- +**| counted and destroyed only when the last table removes a reference to that +**| particular row. (But a row can be emptied of all content no matter how +**| many refs exist, and this might be the next best thing to destruction.) +**| Once a row exists in a least one table (after NewRow() is called), then it +**| can be added to any other table by calling AddRow(), or removed from any +**| table by calling CutRow(), or queried as a member by calling HasRow(). A +**| row can only be added to a table once, and further additions do nothing and +**| complain not at all. Cutting a row from a table only does something when +**| the row was actually a member, and otherwise does nothing silently. +**| +**|| row ref count: one can query the number of tables (and/or cells) +**| containing a row as a member or a child. +**| +**|| row content: one can access or modify the cell content in a table's row +**| by moving content to or from an instance of nsIMdbRow. Note that nsIMdbRow +**| never represents the actual row inside a table, and this is the reason +**| why nsIMdbRow instances do not have row IDs or row scopes. So an instance +**| of nsIMdbRow always and only contains a snapshot of some or all content in +**| past, present, or future persistent row inside a table. This means that +**| reading and writing rows in tables has strictly copy semantics, and we +**| currently do not plan any exceptions for specific performance reasons. +**| +**|| sorting: note all rows are assumed sorted by row ID as a secondary +**| sort following the primary column sort, when table rows are sorted. +**| +**|| indexes: +|*/ + + +#define NS_IMDBTABLE_IID_STR = "fe11bc98-d02b-4128-9fac-87042fdf9639" + +#define NS_IMDBTABLE_IID \ +{0xfe11bc98, 0xd02b, 0x4128, \ +{0x9f, 0xac, 0x87, 0x04, 0x2f, 0xdf, 0x96, 0x39}} + +class nsIMdbTable : public nsIMdbCollection { // a collection of rows +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLE_IID) +// { ===== begin nsIMdbTable methods ===== + + // { ----- begin meta attribute methods ----- + NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) = 0; + NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) = 0; + + NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) = 0; + NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) = 0; + + NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) = 0; + + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0; + + NS_IMETHOD GetMetaRow( + nsIMdbEnv* ev, // context + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mdbOid* outOid, // output meta row oid, can be nil to suppress output + nsIMdbRow** acqRow) = 0; // acquire table's unique singleton meta row + // The purpose of a meta row is to support the persistent recording of + // meta info about a table as cells put into the distinguished meta row. + // Each table has exactly one meta row, which is not considered a member + // of the collection of rows inside the table. The only way to tell + // whether a row is a meta row is by the fact that it is returned by this + // GetMetaRow() method from some table. Otherwise nothing distinguishes + // a meta row from any other row. A meta row can be used anyplace that + // any other row can be used, and can even be put into other tables (or + // the same table) as a table member, if this is useful for some reason. + // The first attempt to access a table's meta row using GetMetaRow() will + // cause the meta row to be created if it did not already exist. When the + // meta row is created, it will have the row oid that was previously + // requested for this table's meta row; or if no oid was ever explicitly + // specified for this meta row, then a unique oid will be generated in + // the row scope named "m" (so obviously MDB clients should not + // manually allocate any row IDs from that special meta scope namespace). + // The meta row oid can be specified either when the table is created, or + // else the first time that GetMetaRow() is called, by passing a non-nil + // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's + // actual oid is returned in outOid (if this is a non-nil pointer), and + // it will be different from inOptionalMetaRowOid when the meta row was + // already given a different oid earlier. + // } ----- end meta attribute methods ----- + + + // { ----- begin cursor methods ----- + NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) = 0; // row oid at the specified position + + NS_IMETHOD OidToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_pos* outPos) = 0; // zero-based ordinal position of row in table + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos + + NS_IMETHOD RowToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_pos* outPos) = 0; // zero-based ordinal position of row in table + // } ----- end row position methods ----- + + // { ----- begin oid set methods ----- + NS_IMETHOD AddOid( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + const mdbOid* inOid) = 0; // row to ensure membership in table + + NS_IMETHOD HasOid( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_bool* outHasOid) = 0; // whether inOid is a member row + + NS_IMETHOD CutOid( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + const mdbOid* inOid) = 0; // row to remove from table + // } ----- end oid set methods ----- + + // { ----- begin row set methods ----- + NS_IMETHOD NewRow( // create a new row instance in table + nsIMdbEnv* ev, // context + mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs + nsIMdbRow** acqRow) = 0; // create new row + + NS_IMETHOD AddRow( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // row to ensure membership in table + + NS_IMETHOD HasRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_bool* outHasRow) = 0; // whether row is a table member + + NS_IMETHOD CutRow( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // row to remove from table + + NS_IMETHOD CutAllRows( // remove all rows from the table + nsIMdbEnv* ev) = 0; // context + // } ----- end row set methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD SearchColumnsHint( // advise re future expected search cols + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = 0; // columns likely to be searched + + NS_IMETHOD SortColumnsHint( // advise re future expected sort columns + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) = 0; // columns for likely sort requests + + NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) = 0; // intend unique address to match end call + // If batch starts nest by virtue of nesting calls in the stack, then + // the address of a local variable makes a good batch start label that + // can be used at batch end time, and such addresses remain unique. + + NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) = 0; // label matching start label + // Suppose a table is maintaining one or many sort orders for a table, + // so that every row added to the table must be inserted in each sort, + // and every row cut must be removed from each sort. If a db client + // intends to make many such changes before needing any information + // about the order or positions of rows inside a table, then a client + // might tell the table to start batch changes in order to disable + // sorting of rows for the interim. Presumably a table will then do + // a full sort of all rows at need when the batch changes end, or when + // a surprise request occurs for row position during batch changes. + // } ----- end hinting methods ----- + + // { ----- begin searching methods ----- + NS_IMETHOD FindRowMatches( // search variable number of sorted cols + nsIMdbEnv* ev, // context + const mdbYarn* inPrefix, // content to find as prefix in row's column cell + nsIMdbTableRowCursor** acqCursor) = 0; // set of matching rows + + NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches() + nsIMdbEnv* ev, // context + mdb_count* outCount, // context + mdbColumnSet* outColSet) = 0; // caller supplied space to put columns + // GetSearchColumns() returns the columns actually searched when the + // FindRowMatches() method is called. No more than mColumnSet_Count + // slots of mColumnSet_Columns will be written, since mColumnSet_Count + // indicates how many slots are present in the column array. The + // actual number of search column used by the table is returned in + // the outCount parameter; if this number exceeds mColumnSet_Count, + // then a caller needs a bigger array to read the entire column set. + // The minimum of mColumnSet_Count and outCount is the number slots + // in mColumnSet_Columns that were actually written by this method. + // + // Callers are expected to change this set of columns by calls to + // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both. + // } ----- end searching methods ----- + + // { ----- begin sorting methods ----- + // sorting: note all rows are assumed sorted by row ID as a secondary + // sort following the primary column sort, when table rows are sorted. + + NS_IMETHOD + CanSortColumn( // query which column is currently used for sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to query sorting potential + mdb_bool* outCanSort) = 0; // whether the column can be sorted + + NS_IMETHOD GetSorting( // view same table in particular sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // requested new column for sorting table + nsIMdbSorting** acqSorting) = 0; // acquire sorting for column + + NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches() + nsIMdbEnv* ev, // context + mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() + nsIMdbSorting* ioSorting) = 0; // requested sorting for some column + // SetSearchSorting() attempts to inform the table that ioSorting + // should be used during calls to FindRowMatches() for searching + // the column which is actually sorted by ioSorting. This method + // is most useful in conjunction with nsIMdbSorting::SetCompare(), + // because otherwise a caller would not be able to override the + // comparison ordering method used during searchs. Note that some + // database implementations might be unable to use an arbitrarily + // specified sort order, either due to schema or runtime interface + // constraints, in which case ioSorting might not actually be used. + // Presumably ioSorting is an instance that was returned from some + // earlier call to nsIMdbTable::GetSorting(). A caller can also + // use nsIMdbTable::SearchColumnsHint() to specify desired change + // in which columns are sorted and searched by FindRowMatches(). + // + // A caller can pass a nil pointer for ioSorting to request that + // column inColumn no longer be used at all by FindRowMatches(). + // But when ioSorting is non-nil, then inColumn should match the + // column actually sorted by ioSorting; when these do not agree, + // implementations are instructed to give precedence to the column + // specified by ioSorting (so this means callers might just pass + // zero for inColumn when ioSorting is also provided, since then + // inColumn is both redundant and ignored). + // } ----- end sorting methods ----- + + // { ----- begin moving methods ----- + // moving a row does nothing unless a table is currently unsorted + + NS_IMETHOD MoveOid( // change position of row in unsorted table + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row inRowId + mdb_pos* outActualPos) = 0; // actual new position of row in table + + NS_IMETHOD MoveRow( // change position of row in unsorted table + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row inRowId + mdb_pos* outActualPos) = 0; // actual new position of row in table + // } ----- end moving methods ----- + + // { ----- begin index methods ----- + NS_IMETHOD AddIndex( // create a sorting index for column if possible + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to sort by index + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index building + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the index addition will be finished. + + NS_IMETHOD CutIndex( // stop supporting a specific column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column with index to be removed + nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index destroy + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the index removal will be finished. + + NS_IMETHOD HasIndex( // query for current presence of a column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outHasIndex) = 0; // whether column has index for this column + + + NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // the column to index if ever sorted + + NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outIndexOnSort) = 0; // whether column has index-on-sort enabled + + NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // the column to index if ever sorted + // } ----- end index methods ----- + +// } ===== end nsIMdbTable methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTable, NS_IMDBTABLE_IID) + +/*| nsIMdbSorting: a view of a table in some particular sort order. This +**| row order closely resembles a readonly array of rows with the same row +**| membership as the underlying table, but in a different order than the +**| table's explicit row order. But the sorting's row membership changes +**| whenever the table's membership changes (without any notification, so +**| keep this in mind when modifying the table). +**| +**|| table: every sorting is associated with a particular table. You +**| cannot change which table is used by a sorting (just ask some new +**| table for a suitable sorting instance instead). +**| +**|| compare: the ordering method used by a sorting, wrapped up in a +**| abstract plug-in interface. When this was never installed by an +**| explicit call to SetNewCompare(), a compare object is still returned, +**| and it might match the compare instance returned by the factory method +**| nsIMdbFactory::MakeCompare(), which represents a default sort order +**| (which we fervently hope is consistently ASCII byte ordering). +**| +**|| cursor: in case callers are more comfortable with a cursor style +**| of accessing row members, each sorting will happily return a cursor +**| instance with behavior very similar to a cursor returned from a call +**| to nsIMdbTable::GetTableRowCursor(), but with different row order. +**| A cursor should show exactly the same information as the pos methods. +**| +**|| pos: the PosToOid() and PosToRow() methods are just like the table +**| methods of the same name, except they show rows in the sort order of +**| the sorting, rather than that of the table. These methods are like +**| readonly array position accessor's, or like a C++ operator[]. +|*/ +class nsIMdbSorting : public nsIMdbObject { // sorting of some table +public: +// { ===== begin nsIMdbSorting methods ===== + + // { ----- begin attribute methods ----- + // sorting: note all rows are assumed sorted by row ID as a secondary + // sort following the primary column sort, when table rows are sorted. + + NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0; + NS_IMETHOD GetSortColumn( // query which col is currently sorted + nsIMdbEnv* ev, // context + mdb_column* outColumn) = 0; // col the table uses for sorting (or zero) + + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetSortingRowCursor( // make a cursor, starting at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance + // A cursor interface turning same info as PosToOid() or PosToRow(). + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) = 0; // row oid at the specified position + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos + // } ----- end row position methods ----- + +// } ===== end nsIMdbSorting methods ===== +}; + +/*| nsIMdbTableRowCursor: cursor class for iterating table rows +**| +**|| table: the cursor is associated with a specific table, which can be +**| set to a different table (which resets the position to -1 so the +**| next row acquired is the first in the table. +**| +**|| NextRowId: the rows in the table can be iterated by identity alone, +**| without actually reading the cells of any row with this method. +**| +**|| NextRowCells: read the next row in the table, but only read cells +**| from the table which are already present in the row (so no new cells +**| are added to the row, even if they are present in the table). All the +**| cells will have content specified, even it is the empty string. No +**| columns will be removed, even if missing from the row (because missing +**| and empty are semantically equivalent). +**| +**|| NextRowAllCells: read the next row in the table, and access all the +**| cells for this row in the table, adding any missing columns to the row +**| as needed until all cells are represented. All the +**| cells will have content specified, even it is the empty string. No +**| columns will be removed, even if missing from the row (because missing +**| and empty are semantically equivalent). +**| +|*/ + +#define NS_IMDBTABLEROWCURSOR_IID_STR = "4f325dad-0385-4b62-a992-c914ab93587e" + +#define NS_IMDBTABLEROWCURSOR_IID \ +{0x4f325dad, 0x0385, 0x4b62, \ +{0xa9, 0x92, 0xc9, 0x14, 0xab, 0x93, 0x58, 0x7e}} + + + +class nsIMdbTableRowCursor : public nsISupports { // table row iterator +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID) + +// { ===== begin nsIMdbTableRowCursor methods ===== + + // { ----- begin attribute methods ----- + // NS_IMETHOD SetTable(nsIMdbEnv* ev, nsIMdbTable* ioTable) = 0; // sets pos to -1 + // Method SetTable() cut and made obsolete in keeping with new sorting methods. + + NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0; + // } ----- end attribute methods ----- + + // { ----- begin duplicate row removal methods ----- + NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups? + mdb_bool* outCanHaveDups) = 0; + + NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* ev, // context + nsIMdbTableRowCursor** acqCursor) = 0; // acquire clone with no dups + // Note that MakeUniqueCursor() is never necessary for a cursor which was + // created by table method nsIMdbTable::GetTableRowCursor(), because a table + // never contains the same row as a member more than once. However, a cursor + // created by table method nsIMdbTable::FindRowMatches() might contain the + // same row more than once, because the same row can generate a hit by more + // than one column with a matching string prefix. Note this method can + // return the very same cursor instance with just an incremented refcount, + // when the original cursor could not contain any duplicate rows (calling + // CanHaveDupRowMembers() shows this case on a false return). Otherwise + // this method returns a different cursor instance. Callers should not use + // this MakeUniqueCursor() method lightly, because it tends to defeat the + // purpose of lazy programming techniques, since it can force creation of + // an explicit row collection in a new cursor's representation, in order to + // inspect the row membership and remove any duplicates; this can have big + // impact if a collection holds tens of thousands of rows or more, when + // the original cursor with dups simply referenced rows indirectly by row + // position ranges, without using an explicit row set representation. + // Callers are encouraged to use nsIMdbCursor::GetCount() to determine + // whether the row collection is very large (tens of thousands), and to + // delay calling MakeUniqueCursor() when possible, until a user interface + // element actually demands the creation of an explicit set representation. + // } ----- end duplicate row removal methods ----- + + // { ----- begin oid iteration methods ----- + NS_IMETHOD NextRowOid( // get row id of next row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD NextRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + + NS_IMETHOD PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD PrevRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) = 0; // zero-based position of the row in table + + // } ----- end row iteration methods ----- + + // { ----- begin copy iteration methods ----- + // NS_IMETHOD NextRowCopy( // put row cells into sink only when already in sink + // nsIMdbEnv* ev, // context + // nsIMdbRow* ioSinkRow, // sink for row cells read from next row + // mdbOid* outOid, // out row oid + // mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // + // NS_IMETHOD NextRowCopyAll( // put all row cells into sink, adding to sink + // nsIMdbEnv* ev, // context + // nsIMdbRow* ioSinkRow, // sink for row cells read from next row + // mdbOid* outOid, // out row oid + // mdb_pos* outRowPos) = 0; // zero-based position of the row in table + // } ----- end copy iteration methods ----- + +// } ===== end nsIMdbTableRowCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTableRowCursor, NS_IMDBTABLEROWCURSOR_IID) + +/*| nsIMdbRow: a collection of cells +**| +|*/ + +#define NS_IMDBROW_IID_STR "271e8d6e-183a-40e3-9f18-36913b4c7853" + + +#define NS_IMDBROW_IID \ +{0x271e8d6e, 0x183a, 0x40e3, \ +{0x9f, 0x18, 0x36, 0x91, 0x3b, 0x4c, 0x78, 0x53}} + + +class nsIMdbRow : public nsIMdbCollection { // cell tuple +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROW_IID) +// { ===== begin nsIMdbRow methods ===== + + // { ----- begin cursor methods ----- + NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inCellPos + nsIMdbEnv* ev, // context + mdb_pos inCellPos, // zero-based ordinal position of cell in row + nsIMdbRowCellCursor** acqCursor) = 0; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin column methods ----- + NS_IMETHOD AddColumn( // make sure a particular column is inside row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) = 0; // cell value to install + + NS_IMETHOD CutColumn( // make sure a column is absent from the row + nsIMdbEnv* ev, // context + mdb_column inColumn) = 0; // column to ensure absent from row + + NS_IMETHOD CutAllColumns( // remove all columns from the row + nsIMdbEnv* ev) = 0; // context + // } ----- end column methods ----- + + // { ----- begin cell methods ----- + NS_IMETHOD NewCell( // get cell for specified column, or add new one + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) = 0; // cell column and value + + NS_IMETHOD AddCell( // copy a cell from another row to this row + nsIMdbEnv* ev, // context + const nsIMdbCell* inCell) = 0; // cell column and value + + NS_IMETHOD GetCell( // find a cell in this row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) = 0; // cell for specified column, or null + + NS_IMETHOD EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* ev) = 0; // context + // } ----- end cell methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD AddRow( // add all cells in another row to this one + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) = 0; // row to union with + + NS_IMETHOD SetRow( // make exact duplicate of another row + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) = 0; // row to duplicate + // } ----- end row methods ----- + + // { ----- begin blob methods ----- + NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn() + mdb_column inColumn, // column to write + const mdbYarn* inYarn) = 0; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to read + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to alias + mdbYarn* outYarn) = 0; // writes ALL yarn slots + + NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // + // The ioColumn argument is an inout parameter which initially contains the + // last column accessed and returns the next column corresponding to the + // content read into the yarn. Callers should start with a zero column + // value to say 'no previous column', which causes the first column to be + // read. Then the value returned in ioColumn is perfect for the next call + // to NextCellYarn(), since it will then be the previous column accessed. + // Callers need only examine the column token returned to see which cell + // in the row is being read into the yarn. When no more columns remain, + // and the iteration has ended, ioColumn will return a zero token again. + // So iterating over cells starts and ends with a zero column token. + + NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // Callers can pass nil for outYarn to indicate no interest in content, so + // only the outColumn value is returned. NOTE to subclasses: you must be + // able to ignore outYarn when the pointer is nil; please do not crash. + + // } ----- end blob methods ----- + +// } ===== end nsIMdbRow methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRow, NS_IMDBROW_IID) + +/*| nsIMdbRowCellCursor: cursor class for iterating row cells +**| +**|| row: the cursor is associated with a specific row, which can be +**| set to a different row (which resets the position to -1 so the +**| next cell acquired is the first in the row. +**| +**|| NextCell: get the next cell in the row and return its position and +**| a new instance of a nsIMdbCell to represent this next cell. +|*/ + +#define NS_IMDBROWCELLCURSOR_IID_STR "b33371a7-5d63-4d10-85a8-e44dffe75c28" + + +#define NS_IMDBROWCELLCURSOR_IID \ +{0x271e8d6e, 0x5d63, 0x4d10 , \ +{0x85, 0xa8, 0xe4, 0x4d, 0xff, 0xe7, 0x5c, 0x28}} + + +class nsIMdbRowCellCursor : public nsISupports{ // cell collection iterator +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROWCELLCURSOR_IID) +// { ===== begin nsIMdbRowCellCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) = 0; // sets pos to -1 + NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) = 0; + // } ----- end attribute methods ----- + + // { ----- begin cell creation methods ----- + NS_IMETHOD MakeCell( // get cell at current pos in the row + nsIMdbEnv* ev, // context + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos, // position of cell in row sequence + nsIMdbCell** acqCell) = 0; // the cell at inPos + // } ----- end cell creation methods ----- + + // { ----- begin cell seeking methods ----- + NS_IMETHOD SeekCell( // same as SetRow() followed by MakeCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) = 0; // the cell at inPos + // } ----- end cell seeking methods ----- + + // { ----- begin cell iteration methods ----- + NS_IMETHOD NextCell( // get next cell in the row + nsIMdbEnv* ev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) = 0; // position of cell in row sequence + + NS_IMETHOD PickNextCell( // get next cell in row within filter set + nsIMdbEnv* ev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) = 0; // position of cell in row sequence + + // Note that inFilterSet should not have too many (many more than 10?) + // cols, since this might imply a potential excessive consumption of time + // over many cursor calls when looking for column and filter intersection. + // } ----- end cell iteration methods ----- + +// } ===== end nsIMdbRowCellCursor methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRowCellCursor, NS_IMDBROWCELLCURSOR_IID) + +/*| nsIMdbBlob: a base class for objects composed mainly of byte sequence state. +**| (This provides a base class for nsIMdbCell, so that cells themselves can +**| be used to set state in another cell, without extracting a buffer.) +|*/ +class nsIMdbBlob : public nsISupports { // a string with associated charset +public: + +// { ===== begin nsIMdbBlob methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetBlob(nsIMdbEnv* ev, + nsIMdbBlob* ioBlob) = 0; // reads inBlob slots + // when inBlob is in the same suite, this might be fastest cell-to-cell + + NS_IMETHOD ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* ev) = 0; + // clearing a yarn is like SetYarn() with empty yarn instance content + + NS_IMETHOD GetBlobFill(nsIMdbEnv* ev, + mdb_fill* outFill) = 0; // size of blob + // Same value that would be put into mYarn_Fill, if one called GetYarn() + // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. + + NS_IMETHOD SetYarn(nsIMdbEnv* ev, + const mdbYarn* inYarn) = 0; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) = 0; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) = 0; // writes ALL yarn slots + // AliasYarn() reveals sensitive internal text buffer state to the caller + // by setting mYarn_Buf to point into the guts of this text implementation. + // + // The caller must take great care to avoid writing on this space, and to + // avoid calling any method that would cause the state of this text object + // to change (say by directly or indirectly setting the text to hold more + // content that might grow the size of the buffer and free the old buffer). + // In particular, callers should scrupulously avoid making calls into the + // mdb interface to write any content while using the buffer pointer found + // in the returned yarn instance. Best safe usage involves copying content + // into some other kind of external content representation beyond mdb. + // + // (The original design of this method a week earlier included the concept + // of very fast and efficient cooperative locking via a pointer to some lock + // member slot. But let's ignore that complexity in the current design.) + // + // AliasYarn() is specifically intended as the first step in transferring + // content from nsIMdbBlob to a nsString representation, without forcing extra + // allocations and/or memory copies. (A standard nsIMdbBlob_AsString() utility + // will use AliasYarn() as the first step in setting a nsString instance.) + // + // This is an alternative to the GetYarn() method, which has copy semantics + // only; AliasYarn() relaxes a robust safety principle only for performance + // reasons, to accomodate the need for callers to transform text content to + // some other canonical representation that would necessitate an additional + // copy and transformation when such is incompatible with the mdbYarn format. + // + // The implementation of AliasYarn() should have extremely little overhead + // besides the virtual dispatch to the method implementation, and the code + // necessary to populate all the mdbYarn member slots with internal buffer + // address and metainformation that describes the buffer content. Note that + // mYarn_Grow must always be set to nil to indicate no resizing is allowed. + + // } ----- end attribute methods ----- + +// } ===== end nsIMdbBlob methods ===== +}; + +/*| nsIMdbCell: the text in a single column of a row. The base nsIMdbBlob +**| class provides all the interface related to accessing cell text. +**| +**|| column: each cell in a row appears in a specific column, where this +**| column is identified by the an integer mdb_scope value (generated by +**| the StringToScopeToken() method in the containing nsIMdbPort instance). +**| Because a row cannot have more than one cell with the same column, +**| something must give if one calls SetColumn() with an existing column +**| in the same row. When this happens, the other cell is replaced with +**| this cell (and the old cell is closed if it has outstanding refs). +**| +**|| row: every cell instance is a part of some row, and every cell knows +**| which row is the parent row. (Note this should be represented by a +**| weak backpointer, so that outstanding cell references cannot keep a +**| row open that should be closed. Otherwise we'd have ref graph cycles.) +**| +**|| text: a cell can either be text, or it can have a child row or table, +**| but not both at once. If text is read from a cell with a child, the text +**| content should be empty (for AliasYarn()) or a description of the type +**| of child (perhaps "mdb:cell:child:row" or "mdb:cell:child:table"). +**| +**|| child: a cell might reference another row or a table, rather than text. +**| The interface for putting and getting children rows and tables was first +**| defined in the nsIMdbTable interface, but then this was moved to this cell +**| interface as more natural. +|*/ + + + +#define NS_IMDBCELL_IID \ +{0xa3b62f71, 0xa181, 0x4a91, \ +{0xb6, 0x6b, 0x27, 0x10, 0x9b, 0x88, 0x98, 0x35}} + +#define NS_IMDBCELL_IID_STR = "a3b62f71-a181-4a91-b66b-27109b889835" + +class nsIMdbCell : public nsIMdbBlob { // text attribute in row with column scope +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID) +// { ===== begin nsIMdbCell methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) = 0; + NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) = 0; + + NS_IMETHOD GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* ev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) = 0; // nonzero if child, and a row child + + // Checking all cell metainfo is a good way to avoid forcing a large cell + // in to memory when you don't actually want to use the content. + + NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell + nsIMdbRow** acqRow) = 0; + NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell + nsIMdbPort** acqPort) = 0; + // } ----- end attribute methods ----- + + // { ----- begin children methods ----- + NS_IMETHOD HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* ev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) = 0; // nonzero if child is a row (rather than a table) + + NS_IMETHOD GetAnyChild( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) = 0; // child table (or null) + + + NS_IMETHOD SetChildRow( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) = 0; // inRow must be bound inside this same db port + + NS_IMETHOD GetChildRow( // access row of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow) = 0; // acquire child row (or nil if no child) + + + NS_IMETHOD SetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable* inTable) = 0; // table must be bound inside this same db port + + NS_IMETHOD GetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) = 0; // acquire child table (or nil if no child) + // } ----- end children methods ----- + +// } ===== end nsIMdbCell methods ===== +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCell, NS_IMDBTABLEROWCURSOR_IID) + +// } %%%%% end C++ abstract class interfaces %%%%% + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MDB_ */ + diff --git a/db/mork/public/moz.build b/db/mork/public/moz.build new file mode 100644 index 0000000000..7212c6effb --- /dev/null +++ b/db/mork/public/moz.build @@ -0,0 +1,9 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'mdb.h', +] + diff --git a/db/mork/src/mork.h b/db/mork/src/mork.h new file mode 100644 index 0000000000..606e8f3d9d --- /dev/null +++ b/db/mork/src/mork.h @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORK_ +#define _MORK_ 1 + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#include "nscore.h" +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +// { %%%%% begin disable unused param warnings %%%%% +#define MORK_USED_1(x) (void)(&x) +#define MORK_USED_2(x,y) (void)(&x);(void)(&y); +#define MORK_USED_3(x,y,z) (void)(&x);(void)(&y);(void)(&z); +#define MORK_USED_4(w,x,y,z) (void)(&w);(void)(&x);(void)(&y);(void)(&z); + +// } %%%%% end disable unused param warnings %%%%% + +// { %%%%% begin macro for finding class member offset %%%%% + +/*| OffsetOf: the unsigned integer offset of a class or struct +**| field from the beginning of that class or struct. This is +**| the same as the similarly named public domain IronDoc macro, +**| and is also the same as another macro appearing in stdlib.h. +**| We want these offsets so we can correctly convert pointers +**| to member slots back into pointers to enclosing objects, and +**| have this exactly match what the compiler thinks is true. +**| +**|| Bascially we are asking the compiler to determine the offset at +**| compile time, and we use the definition of address artithmetic +**| to do this. By casting integer zero to a pointer of type obj*, +**| we can reference the address of a slot in such an object that +**| is hypothetically physically placed at address zero, but without +**| actually dereferencing a memory location. The absolute address +**| of slot is the same as offset of that slot, when the object is +**| placed at address zero. +|*/ +#define mork_OffsetOf(obj,slot) ((unsigned int)&((obj*) 0)->slot) + +// } %%%%% end macro for finding class member offset %%%%% + +// { %%%%% begin specific-size integer scalar typedefs %%%%% +typedef unsigned char mork_u1; // make sure this is one byte +typedef unsigned short mork_u2; // make sure this is two bytes +typedef short mork_i2; // make sure this is two bytes +typedef uint32_t mork_u4; // make sure this is four bytes +typedef int32_t mork_i4; // make sure this is four bytes +typedef PRWord mork_ip; // make sure sizeof(mork_ip) == sizeof(void*) + +typedef mork_u1 mork_ch; // small byte-sized character (never wide) +typedef mork_u1 mork_flags; // one byte's worth of predicate bit flags + +typedef mork_u2 mork_base; // 2-byte magic class signature slot in object +typedef mork_u2 mork_derived; // 2-byte magic class signature slot in object + +typedef mork_u4 mork_token; // unsigned token for atomized string +typedef mork_token mork_scope; // token used to id scope for rows +typedef mork_token mork_kind; // token used to id kind for tables +typedef mork_token mork_cscode; // token used to id charset names +typedef mork_token mork_aid; // token used to id atomize cell values + +typedef mork_token mork_column; // token used to id columns for rows +typedef mork_column mork_delta; // mork_column plus mork_change + +typedef mork_token mork_color; // bead ID +#define morkColor_kNone ((mork_color) 0) + +typedef mork_u4 mork_magic; // unsigned magic signature + +typedef mork_u4 mork_seed; // unsigned collection change counter +typedef mork_u4 mork_count; // unsigned collection member count +typedef mork_count mork_num; // synonym for count +typedef mork_u4 mork_size; // unsigned physical media size +typedef mork_u4 mork_fill; // unsigned logical content size +typedef mork_u4 mork_more; // more available bytes for larger buffer + +typedef mdb_u4 mork_percent; // 0..100, with values >100 same as 100 + +typedef mork_i4 mork_pos; // negative means "before first" (at zero pos) +typedef mork_i4 mork_line; // negative means "before first line in file" + +typedef mork_u1 mork_usage; // 1-byte magic usage signature slot in object +typedef mork_u1 mork_access; // 1-byte magic access signature slot in object + +typedef mork_u1 mork_change; // add, cut, put, set, nil +typedef mork_u1 mork_priority; // 0..9, for a total of ten different values + +typedef mork_u1 mork_able; // on, off, asleep (clone IronDoc's fe_able) +typedef mork_u1 mork_load; // dirty or clean (clone IronDoc's fe_load) +// } %%%%% end specific-size integer scalar typedefs %%%%% + +// 'test' is a public domain Mithril for key equality tests in probe maps +typedef mork_i2 mork_test; /* neg=>kVoid, zero=>kHit, pos=>kMiss */ + +#define morkTest_kVoid ((mork_test) -1) /* -1: nil key slot, no key order */ +#define morkTest_kHit ((mork_test) 0) /* 0: keys are equal, a map hit */ +#define morkTest_kMiss ((mork_test) 1) /* 1: keys not equal, a map miss */ + +// { %%%%% begin constants for Mork scalar types %%%%% +#define morkPriority_kHi ((mork_priority) 0) /* best priority */ +#define morkPriority_kMin ((mork_priority) 0) /* best priority is smallest */ + +#define morkPriority_kLo ((mork_priority) 9) /* worst priority */ +#define morkPriority_kMax ((mork_priority) 9) /* worst priority is biggest */ + +#define morkPriority_kCount 10 /* number of distinct priority values */ + +#define morkAble_kEnabled ((mork_able) 0x55) /* same as IronDoc constant */ +#define morkAble_kDisabled ((mork_able) 0xAA) /* same as IronDoc constant */ +#define morkAble_kAsleep ((mork_able) 0x5A) /* same as IronDoc constant */ + +#define morkChange_kAdd 'a' /* add member */ +#define morkChange_kCut 'c' /* cut member */ +#define morkChange_kPut 'p' /* put member */ +#define morkChange_kSet 's' /* set all members */ +#define morkChange_kNil 0 /* no change in this member */ +#define morkChange_kDup 'd' /* duplicate changes have no effect */ +// kDup is intended to replace another change constant in an object as a +// conclusion about change feasibility while staging intended alterations. + +#define morkLoad_kDirty ((mork_load) 0xDD) /* same as IronDoc constant */ +#define morkLoad_kClean ((mork_load) 0x22) /* same as IronDoc constant */ + +#define morkAccess_kOpen 'o' +#define morkAccess_kClosing 'c' +#define morkAccess_kShut 's' +#define morkAccess_kDead 'd' +// } %%%%% end constants for Mork scalar types %%%%% + +// { %%%%% begin non-specific-size integer scalar typedefs %%%%% +typedef int mork_char; // nominal type for ints used to hold input byte +#define morkChar_IsWhite(c) \ + ((c) == 0xA || (c) == 0x9 || (c) == 0xD || (c) == ' ') +// } %%%%% end non-specific-size integer scalar typedefs %%%%% + +// { %%%%% begin mdb-driven scalar typedefs %%%%% +// easier to define bool exactly the same as mdb: +typedef mdb_bool mork_bool; // unsigned byte with zero=false, nonzero=true + +/* canonical boolean constants provided only for code clarity: */ +#define morkBool_kTrue ((mork_bool) 1) /* actually any nonzero means true */ +#define morkBool_kFalse ((mork_bool) 0) /* only zero means false */ + +// mdb clients can assign these, so we cannot pick maximum size: +typedef mdb_id mork_id; // unsigned object identity in a scope +typedef mork_id mork_rid; // unsigned row identity inside scope +typedef mork_id mork_tid; // unsigned table identity inside scope +typedef mork_id mork_gid; // unsigned group identity without any scope + +// we only care about neg, zero, pos -- so we don't care about size: +typedef mdb_order mork_order; // neg:lessthan, zero:equalto, pos:greaterthan +// } %%%%% end mdb-driven scalar typedefs %%%%% + +#define morkId_kMinusOne ((mdb_id) -1) + +// { %%%%% begin class forward defines %%%%% +// try to put these in alphabetical order for easier examination: +class morkMid; +class morkAtom; +class morkAtomSpace; +class morkBookAtom; +class morkBuf; +class morkBuilder; +class morkCell; +class morkCellObject; +class morkCursor; +class morkEnv; +class morkFactory; +class morkFile; +class morkHandle; +class morkHandleFace; // just an opaque cookie type +class morkHandleFrame; +class morkHashArrays; +class morkMap; +class morkNode; +class morkObject; +class morkOidAtom; +class morkParser; +class morkPool; +class morkPlace; +class morkPort; +class morkPortTableCursor; +class morkProbeMap; +class morkRow; +class morkRowCellCursor; +class morkRowObject; +class morkRowSpace; +class morkSorting; +class morkSortingRowCursor; +class morkSpace; +class morkSpan; +class morkStore; +class morkStream; +class morkTable; +class morkTableChange; +class morkTableRowCursor; +class morkThumb; +class morkWriter; +class morkZone; +// } %%%%% end class forward defines %%%%% + +// include this config file last for platform & environment specific stuff: +#ifndef _MORKCONFIG_ +#include "morkConfig.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORK_ */ diff --git a/db/mork/src/morkArray.cpp b/db/mork/src/morkArray.cpp new file mode 100644 index 0000000000..5b83b85e78 --- /dev/null +++ b/db/mork/src/morkArray.cpp @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKARRAY_ +#include "morkArray.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkArray::CloseMorkNode(morkEnv* ev) // CloseTable() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseArray(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkArray::~morkArray() // assert CloseTable() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mArray_Slots==0); +} + +/*public non-poly*/ +morkArray::morkArray(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap) +: morkNode(ev, inUsage, ioHeap) +, mArray_Slots( 0 ) +, mArray_Heap( 0 ) +, mArray_Fill( 0 ) +, mArray_Size( 0 ) +, mArray_Seed( (mork_u4)NS_PTR_TO_INT32(this) ) // "random" integer assignment +{ + if ( ev->Good() ) + { + if ( ioSlotHeap ) + { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mArray_Heap); + if ( ev->Good() ) + { + if ( inSize < 3 ) + inSize = 3; + mdb_size byteSize = inSize * sizeof(void*); + void** block = 0; + ioSlotHeap->Alloc(ev->AsMdbEnv(), byteSize, (void**) &block); + if ( block && ev->Good() ) + { + mArray_Slots = block; + mArray_Size = inSize; + MORK_MEMSET(mArray_Slots, 0, byteSize); + if ( ev->Good() ) + mNode_Derived = morkDerived_kArray; + } + } + } + else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void +morkArray::CloseArray(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + if ( mArray_Heap && mArray_Slots ) + mArray_Heap->Free(ev->AsMdbEnv(), mArray_Slots); + + mArray_Slots = 0; + mArray_Size = 0; + mArray_Fill = 0; + ++mArray_Seed; + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mArray_Heap); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkArray::NonArrayTypeError(morkEnv* ev) +{ + ev->NewError("non morkArray"); +} + +/*static*/ void +morkArray::IndexBeyondEndError(morkEnv* ev) +{ + ev->NewError("array index beyond end"); +} + +/*static*/ void +morkArray::NilSlotsAddressError(morkEnv* ev) +{ + ev->NewError("nil mArray_Slots"); +} + +/*static*/ void +morkArray::FillBeyondSizeError(morkEnv* ev) +{ + ev->NewError("mArray_Fill > mArray_Size"); +} + +mork_bool +morkArray::Grow(morkEnv* ev, mork_size inNewSize) +// Grow() returns true if capacity becomes >= inNewSize and ev->Good() +{ + if ( ev->Good() && inNewSize > mArray_Size ) // make array larger? + { + if ( mArray_Fill <= mArray_Size ) // fill and size fit the invariant? + { + if (mArray_Size <= 3) + inNewSize = mArray_Size + 3; + else + inNewSize = mArray_Size * 2;// + 3; // try doubling size here - used to grow by 3 + + mdb_size newByteSize = inNewSize * sizeof(void*); + void** newBlock = 0; + mArray_Heap->Alloc(ev->AsMdbEnv(), newByteSize, (void**) &newBlock); + if ( newBlock && ev->Good() ) // okay new block? + { + void** oldSlots = mArray_Slots; + void** oldEnd = oldSlots + mArray_Fill; + + void** newSlots = newBlock; + void** newEnd = newBlock + inNewSize; + + while ( oldSlots < oldEnd ) + *newSlots++ = *oldSlots++; + + while ( newSlots < newEnd ) + *newSlots++ = (void*) 0; + + oldSlots = mArray_Slots; + mArray_Size = inNewSize; + mArray_Slots = newBlock; + mArray_Heap->Free(ev->AsMdbEnv(), oldSlots); + } + } + else + this->FillBeyondSizeError(ev); + } + ++mArray_Seed; // always modify seed, since caller intends to add slots + return ( ev->Good() && mArray_Size >= inNewSize ); +} + +void* +morkArray::SafeAt(morkEnv* ev, mork_pos inPos) +{ + if ( mArray_Slots ) + { + if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill ) + return mArray_Slots[ inPos ]; + else + this->IndexBeyondEndError(ev); + } + else + this->NilSlotsAddressError(ev); + + return (void*) 0; +} + +void +morkArray::SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot) +{ + if ( mArray_Slots ) + { + if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill ) + { + mArray_Slots[ inPos ] = ioSlot; + ++mArray_Seed; + } + else + this->IndexBeyondEndError(ev); + } + else + this->NilSlotsAddressError(ev); +} + +mork_pos +morkArray::AppendSlot(morkEnv* ev, void* ioSlot) +{ + mork_pos outPos = -1; + if ( mArray_Slots ) + { + mork_fill fill = mArray_Fill; + if ( this->Grow(ev, fill+1) ) + { + outPos = (mork_pos) fill; + mArray_Slots[ fill ] = ioSlot; + mArray_Fill = fill + 1; + // note Grow() increments mArray_Seed + } + } + else + this->NilSlotsAddressError(ev); + + return outPos; +} + +void +morkArray::AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot) +{ + if ( mArray_Slots ) + { + mork_fill fill = mArray_Fill; + if ( this->Grow(ev, fill+1) ) + { + void** slot = mArray_Slots; // the slot vector + void** end = slot + fill; // one past the last used array slot + slot += inPos; // the slot to be added + + while ( --end >= slot ) // another slot to move upward? + end[ 1 ] = *end; + + *slot = ioSlot; + mArray_Fill = fill + 1; + // note Grow() increments mArray_Seed + } + } + else + this->NilSlotsAddressError(ev); +} + +void +morkArray::CutSlot(morkEnv* ev, mork_pos inPos) +{ + MORK_USED_1(ev); + mork_fill fill = mArray_Fill; + if ( inPos >= 0 && inPos < (mork_pos) fill ) // cutting slot in used array portion? + { + void** slot = mArray_Slots; // the slot vector + void** end = slot + fill; // one past the last used array slot + slot += inPos; // the slot to be cut + + while ( ++slot < end ) // another slot to move downward? + slot[ -1 ] = *slot; + + slot[ -1 ] = 0; // clear the last used slot which is now unused + + // note inPos0, so fill-1 must be nonnegative: + mArray_Fill = fill - 1; + ++mArray_Seed; + } +} + +void +morkArray::CutAllSlots(morkEnv* ev) +{ + if ( mArray_Slots ) + { + if ( mArray_Fill <= mArray_Size ) + { + mdb_size oldByteSize = mArray_Fill * sizeof(void*); + MORK_MEMSET(mArray_Slots, 0, oldByteSize); + } + else + this->FillBeyondSizeError(ev); + } + else + this->NilSlotsAddressError(ev); + + ++mArray_Seed; + mArray_Fill = 0; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkArray.h b/db/mork/src/morkArray.h new file mode 100644 index 0000000000..d987b5d258 --- /dev/null +++ b/db/mork/src/morkArray.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKARRAY_ +#define _MORKARRAY_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kArray /*i*/ 0x4179 /* ascii 'Ay' */ + +class morkArray : public morkNode { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + void** mArray_Slots; // array of pointers + nsIMdbHeap* mArray_Heap; // required heap for allocating mArray_Slots + mork_fill mArray_Fill; // logical count of used slots in mArray_Slots + mork_size mArray_Size; // physical count of mArray_Slots ( >= Fill) + mork_seed mArray_Seed; // change counter for syncing with iterators + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseArray() + virtual ~morkArray(); // assert that close executed earlier + +public: // morkArray construction & destruction + morkArray(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap); + void CloseArray(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkArray(const morkArray& other); + morkArray& operator=(const morkArray& other); + +public: // dynamic type identification + mork_bool IsArray() const + { return IsNode() && mNode_Derived == morkDerived_kArray; } +// } ===== end morkNode methods ===== + +public: // typing & errors + static void NonArrayTypeError(morkEnv* ev); + static void IndexBeyondEndError(morkEnv* ev); + static void NilSlotsAddressError(morkEnv* ev); + static void FillBeyondSizeError(morkEnv* ev); + +public: // other table row cursor methods + + mork_fill Length() const { return mArray_Fill; } + mork_size Capacity() const { return mArray_Size; } + + mork_bool Grow(morkEnv* ev, mork_size inNewSize); + // Grow() returns true if capacity becomes >= inNewSize and ev->Good() + + void* At(mork_pos inPos) const { return mArray_Slots[ inPos ]; } + void AtPut(mork_pos inPos, void* ioSlot) + { mArray_Slots[ inPos ] = ioSlot; } + + void* SafeAt(morkEnv* ev, mork_pos inPos); + void SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot); + + mork_pos AppendSlot(morkEnv* ev, void* ioSlot); + void AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot); + void CutSlot(morkEnv* ev, mork_pos inPos); + void CutAllSlots(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakArray(morkArray* me, + morkEnv* ev, morkArray** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongArray(morkArray* me, + morkEnv* ev, morkArray** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/db/mork/src/morkAtom.cpp b/db/mork/src/morkAtom.cpp new file mode 100644 index 0000000000..0b73f97fae --- /dev/null +++ b/db/mork/src/morkAtom.cpp @@ -0,0 +1,528 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +mork_bool +morkAtom::GetYarn(mdbYarn* outYarn) const +{ + const void* source = 0; + mdb_fill fill = 0; + mdb_cscode form = 0; + outYarn->mYarn_More = 0; + + if ( this->IsWeeBook() ) + { + morkWeeBookAtom* weeBook = (morkWeeBookAtom*) this; + source = weeBook->mWeeBookAtom_Body; + fill = weeBook->mAtom_Size; + } + else if ( this->IsBigBook() ) + { + morkBigBookAtom* bigBook = (morkBigBookAtom*) this; + source = bigBook->mBigBookAtom_Body; + fill = bigBook->mBigBookAtom_Size; + form = bigBook->mBigBookAtom_Form; + } + else if ( this->IsWeeAnon() ) + { + morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) this; + source = weeAnon->mWeeAnonAtom_Body; + fill = weeAnon->mAtom_Size; + } + else if ( this->IsBigAnon() ) + { + morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) this; + source = bigAnon->mBigAnonAtom_Body; + fill = bigAnon->mBigAnonAtom_Size; + form = bigAnon->mBigAnonAtom_Form; + } + + if ( source && fill ) // have an atom with nonempty content? + { + // if we have too many bytes, and yarn seems growable: + if ( fill > outYarn->mYarn_Size && outYarn->mYarn_Grow ) // try grow? + (*outYarn->mYarn_Grow)(outYarn, (mdb_size) fill); // request bigger + + mdb_size size = outYarn->mYarn_Size; // max dest size + if ( fill > size ) // too much atom content? + { + outYarn->mYarn_More = fill - size; // extra atom bytes omitted + fill = size; // copy no more bytes than size of yarn buffer + } + void* dest = outYarn->mYarn_Buf; // where bytes are going + if ( !dest ) // nil destination address buffer? + fill = 0; // we can't write any content at all + + if ( fill ) // anything to copy? + MORK_MEMCPY(dest, source, fill); // copy fill bytes to yarn + + outYarn->mYarn_Fill = fill; // tell yarn size of copied content + } + else // no content to put into the yarn + { + outYarn->mYarn_Fill = 0; // tell yarn that atom has no bytes + } + outYarn->mYarn_Form = form; // always update the form slot + + return ( source != 0 ); +} + +/* static */ +mork_bool +morkAtom::AliasYarn(const morkAtom* atom, mdbYarn* outYarn) +{ + outYarn->mYarn_More = 0; + outYarn->mYarn_Form = 0; + + if ( atom ) + { + if ( atom->IsWeeBook() ) + { + morkWeeBookAtom* weeBook = (morkWeeBookAtom*) atom; + outYarn->mYarn_Buf = weeBook->mWeeBookAtom_Body; + outYarn->mYarn_Fill = weeBook->mAtom_Size; + outYarn->mYarn_Size = weeBook->mAtom_Size; + } + else if ( atom->IsBigBook() ) + { + morkBigBookAtom* bigBook = (morkBigBookAtom*) atom; + outYarn->mYarn_Buf = bigBook->mBigBookAtom_Body; + outYarn->mYarn_Fill = bigBook->mBigBookAtom_Size; + outYarn->mYarn_Size = bigBook->mBigBookAtom_Size; + outYarn->mYarn_Form = bigBook->mBigBookAtom_Form; + } + else if ( atom->IsWeeAnon() ) + { + morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) atom; + outYarn->mYarn_Buf = weeAnon->mWeeAnonAtom_Body; + outYarn->mYarn_Fill = weeAnon->mAtom_Size; + outYarn->mYarn_Size = weeAnon->mAtom_Size; + } + else if ( atom->IsBigAnon() ) + { + morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) atom; + outYarn->mYarn_Buf = bigAnon->mBigAnonAtom_Body; + outYarn->mYarn_Fill = bigAnon->mBigAnonAtom_Size; + outYarn->mYarn_Size = bigAnon->mBigAnonAtom_Size; + outYarn->mYarn_Form = bigAnon->mBigAnonAtom_Form; + } + else + atom = 0; // show desire to put empty content in yarn + } + + if ( !atom ) // empty content for yarn? + { + outYarn->mYarn_Buf = 0; + outYarn->mYarn_Fill = 0; + outYarn->mYarn_Size = 0; + // outYarn->mYarn_Grow = 0; // please don't modify the Grow slot + } + return ( atom != 0 ); +} + +mork_aid +morkAtom::GetBookAtomAid() const // zero or book atom's ID +{ + return ( this->IsBook() )? ((morkBookAtom*) this)->mBookAtom_Id : 0; +} + +mork_scope +morkAtom::GetBookAtomSpaceScope(morkEnv* ev) const // zero or book's space's scope +{ + mork_scope outScope = 0; + if ( this->IsBook() ) + { + const morkBookAtom* bookAtom = (const morkBookAtom*) this; + morkAtomSpace* space = bookAtom->mBookAtom_Space; + if ( space->IsAtomSpace() ) + outScope = space->SpaceScope(); + else + space->NonAtomSpaceTypeError(ev); + } + + return outScope; +} + +void +morkAtom::MakeCellUseForever(morkEnv* ev) +{ + MORK_USED_1(ev); + mAtom_CellUses = morkAtom_kForeverCellUses; +} + +mork_u1 +morkAtom::AddCellUse(morkEnv* ev) +{ + MORK_USED_1(ev); + if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not already maxed out? + ++mAtom_CellUses; + + return mAtom_CellUses; +} + +mork_u1 +morkAtom::CutCellUse(morkEnv* ev) +{ + if ( mAtom_CellUses ) // any outstanding uses to cut? + { + if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not frozen at max? + --mAtom_CellUses; + } + else + this->CellUsesUnderflowWarning(ev); + + return mAtom_CellUses; +} + +/*static*/ void +morkAtom::CellUsesUnderflowWarning(morkEnv* ev) +{ + ev->NewWarning("mAtom_CellUses underflow"); +} + +/*static*/ void +morkAtom::BadAtomKindError(morkEnv* ev) +{ + ev->NewError("bad mAtom_Kind"); +} + +/*static*/ void +morkAtom::ZeroAidError(morkEnv* ev) +{ + ev->NewError("zero atom ID"); +} + +/*static*/ void +morkAtom::AtomSizeOverflowError(morkEnv* ev) +{ + ev->NewError("atom mAtom_Size overflow"); +} + +void +morkOidAtom::InitRowOidAtom(morkEnv* ev, const mdbOid& inOid) +{ + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindRowOid; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mOidAtom_Oid = inOid; // bitwise copy +} + +void +morkOidAtom::InitTableOidAtom(morkEnv* ev, const mdbOid& inOid) +{ + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindTableOid; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mOidAtom_Oid = inOid; // bitwise copy +} + +void +morkWeeAnonAtom::InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf) +{ + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize ) + { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindWeeAnon; + mork_size size = inBuf.mBuf_Fill; + mAtom_Size = (mork_u1) size; + if ( size && inBuf.mBuf_Body ) + MORK_MEMCPY(mWeeAnonAtom_Body, inBuf.mBuf_Body, size); + + mWeeAnonAtom_Body[ size ] = 0; + } + else + this->AtomSizeOverflowError(ev); +} + +void +morkBigAnonAtom::InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm) +{ + MORK_USED_1(ev); + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindBigAnon; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + mBigAnonAtom_Form = inForm; + mork_size size = inBuf.mBuf_Fill; + mBigAnonAtom_Size = size; + if ( size && inBuf.mBuf_Body ) + MORK_MEMCPY(mBigAnonAtom_Body, inBuf.mBuf_Body, size); + + mBigAnonAtom_Body[ size ] = 0; +} + +/*static*/ void +morkBookAtom::NonBookAtomTypeError(morkEnv* ev) +{ + ev->NewError("non morkBookAtom"); +} + +mork_u4 +morkBookAtom::HashFormAndBody(morkEnv* ev) const +{ + // This hash is obviously a variation of the dragon book string hash. + // (I won't bother to explain or rationalize this usage for you.) + + mork_u4 outHash = 0; // hash value returned + unsigned char c; // next character + const mork_u1* body; // body of bytes to hash + mork_size size = 0; // the number of bytes to hash + + if ( this->IsWeeBook() ) + { + size = mAtom_Size; + body = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body; + } + else if ( this->IsBigBook() ) + { + size = ((const morkBigBookAtom*) this)->mBigBookAtom_Size; + body = ((const morkBigBookAtom*) this)->mBigBookAtom_Body; + } + else if ( this->IsFarBook() ) + { + size = ((const morkFarBookAtom*) this)->mFarBookAtom_Size; + body = ((const morkFarBookAtom*) this)->mFarBookAtom_Body; + } + else + { + this->NonBookAtomTypeError(ev); + return 0; + } + + const mork_u1* end = body + size; + while ( body < end ) + { + c = *body++; + outHash <<= 4; + outHash += c; + mork_u4 top = outHash & 0xF0000000L; // top four bits + if ( top ) // any of high four bits equal to one? + { + outHash ^= (top >> 24); // fold down high bits + outHash ^= top; // zero top four bits + } + } + + return outHash; +} + +mork_bool +morkBookAtom::EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const +{ + mork_bool outEqual = morkBool_kFalse; + + const mork_u1* body = 0; // body of inAtom bytes to compare + mork_size size; // the number of inAtom bytes to compare + mork_cscode form; // nominal charset for ioAtom + + if ( inAtom->IsWeeBook() ) + { + size = inAtom->mAtom_Size; + body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body; + form = 0; + } + else if ( inAtom->IsBigBook() ) + { + size = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Size; + body = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Body; + form = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Form; + } + else if ( inAtom->IsFarBook() ) + { + size = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Size; + body = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Body; + form = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Form; + } + else + { + inAtom->NonBookAtomTypeError(ev); + return morkBool_kFalse; + } + + const mork_u1* thisBody = 0; // body of bytes in this to compare + mork_size thisSize; // the number of bytes in this to compare + mork_cscode thisForm; // nominal charset for this atom + + if ( this->IsWeeBook() ) + { + thisSize = mAtom_Size; + thisBody = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body; + thisForm = 0; + } + else if ( this->IsBigBook() ) + { + thisSize = ((const morkBigBookAtom*) this)->mBigBookAtom_Size; + thisBody = ((const morkBigBookAtom*) this)->mBigBookAtom_Body; + thisForm = ((const morkBigBookAtom*) this)->mBigBookAtom_Form; + } + else if ( this->IsFarBook() ) + { + thisSize = ((const morkFarBookAtom*) this)->mFarBookAtom_Size; + thisBody = ((const morkFarBookAtom*) this)->mFarBookAtom_Body; + thisForm = ((const morkFarBookAtom*) this)->mFarBookAtom_Form; + } + else + { + this->NonBookAtomTypeError(ev); + return morkBool_kFalse; + } + + // if atoms are empty, form is irrelevant + if ( body && thisBody && size == thisSize && (!size || form == thisForm )) + outEqual = (MORK_MEMCMP(body, thisBody, size) == 0); + + return outEqual; +} + + +void +morkBookAtom::CutBookAtomFromSpace(morkEnv* ev) +{ + morkAtomSpace* space = mBookAtom_Space; + if ( space ) + { + mBookAtom_Space = 0; + space->mAtomSpace_AtomBodies.CutAtom(ev, this); + space->mAtomSpace_AtomAids.CutAtom(ev, this); + } + else + ev->NilPointerError(); +} + +morkWeeBookAtom::morkWeeBookAtom(mork_aid inAid) +{ + mAtom_Kind = morkAtom_kKindWeeBook; + mAtom_CellUses = 0; + mAtom_Change = morkChange_kNil; + mAtom_Size = 0; + + mBookAtom_Space = 0; + mBookAtom_Id = inAid; + + mWeeBookAtom_Body[ 0 ] = 0; +} + +void +morkWeeBookAtom::InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf, + morkAtomSpace* ioSpace, mork_aid inAid) +{ + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if ( ioSpace ) + { + if ( inAid ) + { + if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize ) + { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindWeeBook; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mork_size size = inBuf.mBuf_Fill; + mAtom_Size = (mork_u1) size; + if ( size && inBuf.mBuf_Body ) + MORK_MEMCPY(mWeeBookAtom_Body, inBuf.mBuf_Body, size); + + mWeeBookAtom_Body[ size ] = 0; + } + else + this->AtomSizeOverflowError(ev); + } + else + this->ZeroAidError(ev); + } + else + ev->NilPointerError(); +} + +void +morkBigBookAtom::InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid) +{ + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if ( ioSpace ) + { + if ( inAid ) + { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindBigBook; + mAtom_Size = 0; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mBigBookAtom_Form = inForm; + mork_size size = inBuf.mBuf_Fill; + mBigBookAtom_Size = size; + if ( size && inBuf.mBuf_Body ) + MORK_MEMCPY(mBigBookAtom_Body, inBuf.mBuf_Body, size); + + mBigBookAtom_Body[ size ] = 0; + } + else + this->ZeroAidError(ev); + } + else + ev->NilPointerError(); +} + +void morkFarBookAtom::InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid) +{ + mAtom_Kind = 0; + mAtom_Change = morkChange_kNil; + if ( ioSpace ) + { + if ( inAid ) + { + mAtom_CellUses = 0; + mAtom_Kind = morkAtom_kKindFarBook; + mAtom_Size = 0; + mBookAtom_Space = ioSpace; + mBookAtom_Id = inAid; + mFarBookAtom_Form = inForm; + mFarBookAtom_Size = inBuf.mBuf_Fill; + mFarBookAtom_Body = (mork_u1*) inBuf.mBuf_Body; + } + else + this->ZeroAidError(ev); + } + else + ev->NilPointerError(); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkAtom.h b/db/mork/src/morkAtom.h new file mode 100644 index 0000000000..68d4828de6 --- /dev/null +++ b/db/mork/src/morkAtom.h @@ -0,0 +1,365 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKATOM_ +#define _MORKATOM_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +#define morkAtom_kMaxByteSize 255 /* max for 8-bit integer */ +#define morkAtom_kForeverCellUses 0x0FF /* max for 8-bit integer */ +#define morkAtom_kMaxCellUses 0x07F /* max for 7-bit integer */ + +#define morkAtom_kKindWeeAnon 'a' /* means morkWeeAnonAtom subclass */ +#define morkAtom_kKindBigAnon 'A' /* means morkBigAnonAtom subclass */ +#define morkAtom_kKindWeeBook 'b' /* means morkWeeBookAtom subclass */ +#define morkAtom_kKindBigBook 'B' /* means morkBigBookAtom subclass */ +#define morkAtom_kKindFarBook 'f' /* means morkFarBookAtom subclass */ +#define morkAtom_kKindRowOid 'r' /* means morkOidAtom subclass */ +#define morkAtom_kKindTableOid 't' /* means morkOidAtom subclass */ + +/*| Atom: . +|*/ +class morkAtom { // + +public: + + mork_u1 mAtom_Kind; // identifies a specific atom subclass + mork_u1 mAtom_CellUses; // number of persistent uses in a cell + mork_change mAtom_Change; // how has this atom been changed? + mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + +public: + morkAtom(mork_aid inAid, mork_u1 inKind); + + mork_bool IsWeeAnon() const { return mAtom_Kind == morkAtom_kKindWeeAnon; } + mork_bool IsBigAnon() const { return mAtom_Kind == morkAtom_kKindBigAnon; } + mork_bool IsWeeBook() const { return mAtom_Kind == morkAtom_kKindWeeBook; } + mork_bool IsBigBook() const { return mAtom_Kind == morkAtom_kKindBigBook; } + mork_bool IsFarBook() const { return mAtom_Kind == morkAtom_kKindFarBook; } + mork_bool IsRowOid() const { return mAtom_Kind == morkAtom_kKindRowOid; } + mork_bool IsTableOid() const { return mAtom_Kind == morkAtom_kKindTableOid; } + + mork_bool IsBook() const { return this->IsWeeBook() || this->IsBigBook(); } + +public: // clean vs dirty + + void SetAtomClean() { mAtom_Change = morkChange_kNil; } + void SetAtomDirty() { mAtom_Change = morkChange_kAdd; } + + mork_bool IsAtomClean() const { return mAtom_Change == morkChange_kNil; } + mork_bool IsAtomDirty() const { return mAtom_Change == morkChange_kAdd; } + +public: // atom space scope if IsBook() is true, or else zero: + + mork_scope GetBookAtomSpaceScope(morkEnv* ev) const; + // zero or book's space's scope + + mork_aid GetBookAtomAid() const; + // zero or book atom's ID + +public: // empty construction does nothing + morkAtom() { } + +public: // one-byte refcounting, freezing at maximum + void MakeCellUseForever(morkEnv* ev); + mork_u1 AddCellUse(morkEnv* ev); + mork_u1 CutCellUse(morkEnv* ev); + + mork_bool IsCellUseForever() const + { return mAtom_CellUses == morkAtom_kForeverCellUses; } + +private: // warnings + + static void CellUsesUnderflowWarning(morkEnv* ev); + +public: // errors + + static void BadAtomKindError(morkEnv* ev); + static void ZeroAidError(morkEnv* ev); + static void AtomSizeOverflowError(morkEnv* ev); + +public: // yarns + + static mork_bool AliasYarn(const morkAtom* atom, mdbYarn* outYarn); + mork_bool GetYarn(mdbYarn* outYarn) const; + +private: // copying is not allowed + morkAtom(const morkAtom& other); + morkAtom& operator=(const morkAtom& other); +}; + +/*| OidAtom: an atom that references a row or table by identity. +|*/ +class morkOidAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + +public: + mdbOid mOidAtom_Oid; // identity of referenced object + +public: // empty construction does nothing + morkOidAtom() { } + void InitRowOidAtom(morkEnv* ev, const mdbOid& inOid); + void InitTableOidAtom(morkEnv* ev, const mdbOid& inOid); + +private: // copying is not allowed + morkOidAtom(const morkOidAtom& other); + morkOidAtom& operator=(const morkOidAtom& other); +}; + +/*| WeeAnonAtom: an atom whose content immediately follows morkAtom slots +**| in an inline fashion, so that morkWeeAnonAtom contains both leading +**| atom slots and then the content bytes without further overhead. Note +**| that charset encoding is not indicated, so zero is implied for Latin1. +**| (Non-Latin1 content must be stored in a morkBigAnonAtom with a charset.) +**| +**|| An anon (anonymous) atom has no identity, with no associated bookkeeping +**| for lookup needed for sharing like a book atom. +**| +**|| A wee anon atom is immediate but not shared with any other users of this +**| atom, so no bookkeeping for sharing is needed. This means the atom has +**| no ID, because the atom has no identity other than this immediate content, +**| and no hash table is needed to look up this particular atom. This also +**| applies to the larger format morkBigAnonAtom, which has more slots. +|*/ +class morkWeeAnonAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + +public: + mork_u1 mWeeAnonAtom_Body[ 1 ]; // 1st byte of immediate content vector + +public: // empty construction does nothing + morkWeeAnonAtom() { } + void InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) + { return sizeof(morkWeeAnonAtom) + inFill; } + +private: // copying is not allowed + morkWeeAnonAtom(const morkWeeAnonAtom& other); + morkWeeAnonAtom& operator=(const morkWeeAnonAtom& other); +}; + +/*| BigAnonAtom: another immediate atom that cannot be encoded as the smaller +**| morkWeeAnonAtom format because either the size is too great, and/or the +**| charset is not the default zero for Latin1 and must be explicitly noted. +**| +**|| An anon (anonymous) atom has no identity, with no associated bookkeeping +**| for lookup needed for sharing like a book atom. +|*/ +class morkBigAnonAtom : public morkAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + +public: + mork_cscode mBigAnonAtom_Form; // charset format encoding + mork_size mBigAnonAtom_Size; // size of content vector + mork_u1 mBigAnonAtom_Body[ 1 ]; // 1st byte of immed content vector + +public: // empty construction does nothing + morkBigAnonAtom() { } + void InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) + { return sizeof(morkBigAnonAtom) + inFill; } + +private: // copying is not allowed + morkBigAnonAtom(const morkBigAnonAtom& other); + morkBigAnonAtom& operator=(const morkBigAnonAtom& other); +}; + +#define morkBookAtom_kMaxBodySize 1024 /* if larger, cannot be shared */ + +/*| BookAtom: the common subportion of wee book atoms and big book atoms that +**| includes the atom ID and the pointer to the space referencing this atom +**| through a hash table. +|*/ +class morkBookAtom : public morkAtom { // + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + +public: + morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is atom scope + mork_aid mBookAtom_Id; // identity token for this shared atom + +public: // empty construction does nothing + morkBookAtom() { } + + static void NonBookAtomTypeError(morkEnv* ev); + +public: // Hash() and Equal() for atom ID maps are same for all subclasses: + + mork_u4 HashAid() const { return mBookAtom_Id; } + mork_bool EqualAid(const morkBookAtom* inAtom) const + { return ( mBookAtom_Id == inAtom->mBookAtom_Id); } + +public: // Hash() and Equal() for atom body maps know about subclasses: + + // YOU CANNOT SUBCLASS morkBookAtom WITHOUT FIXING Hash and Equal METHODS: + + mork_u4 HashFormAndBody(morkEnv* ev) const; + mork_bool EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const; + +public: // separation from containing space + + void CutBookAtomFromSpace(morkEnv* ev); + +private: // copying is not allowed + morkBookAtom(const morkBookAtom& other); + morkBookAtom& operator=(const morkBookAtom& other); +}; + +/*| FarBookAtom: this alternative format for book atoms was introduced +**| in May 2000 in order to support finding atoms in hash tables without +**| first copying the strings from original parsing buffers into a new +**| atom format. This was consuming too much time. However, we can +**| use morkFarBookAtom to stage a hash table query, as long as we then +**| fix HashFormAndBody() and EqualFormAndBody() to use morkFarBookAtom +**| correctly. +**| +**|| Note we do NOT intend that instances of morkFarBookAtom will ever +**| be installed in hash tables, because this is not space efficient. +**| We only expect to create temp instances for table lookups. +|*/ +class morkFarBookAtom : public morkBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + +public: + mork_cscode mFarBookAtom_Form; // charset format encoding + mork_size mFarBookAtom_Size; // size of content vector + mork_u1* mFarBookAtom_Body; // bytes are elsewere, out of line + +public: // empty construction does nothing + morkFarBookAtom() { } + void InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid); + +private: // copying is not allowed + morkFarBookAtom(const morkFarBookAtom& other); + morkFarBookAtom& operator=(const morkFarBookAtom& other); +}; + +/*| WeeBookAtom: . +|*/ +class morkWeeBookAtom : public morkBookAtom { // + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + +public: + mork_u1 mWeeBookAtom_Body[ 1 ]; // 1st byte of immed content vector + +public: // empty construction does nothing + morkWeeBookAtom() { } + morkWeeBookAtom(mork_aid inAid); + + void InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf, + morkAtomSpace* ioSpace, mork_aid inAid); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) + { return sizeof(morkWeeBookAtom) + inFill; } + +private: // copying is not allowed + morkWeeBookAtom(const morkWeeBookAtom& other); + morkWeeBookAtom& operator=(const morkWeeBookAtom& other); +}; + +/*| BigBookAtom: . +|*/ +class morkBigBookAtom : public morkBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + +public: + mork_cscode mBigBookAtom_Form; // charset format encoding + mork_size mBigBookAtom_Size; // size of content vector + mork_u1 mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector + +public: // empty construction does nothing + morkBigBookAtom() { } + void InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid); + + // allow extra trailing byte for a null byte: + static mork_size SizeForFill(mork_fill inFill) + { return sizeof(morkBigBookAtom) + inFill; } + +private: // copying is not allowed + morkBigBookAtom(const morkBigBookAtom& other); + morkBigBookAtom& operator=(const morkBigBookAtom& other); +}; + +/*| MaxBookAtom: . +|*/ +class morkMaxBookAtom : public morkBigBookAtom { // + + // mork_u1 mAtom_Kind; // identifies a specific atom subclass + // mork_u1 mAtom_CellUses; // number of persistent uses in a cell + // mork_change mAtom_Change; // how has this atom been changed? + // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms + + // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope + // mork_aid mBookAtom_Id; // identity token for this shared atom + + // mork_cscode mBigBookAtom_Form; // charset format encoding + // mork_size mBigBookAtom_Size; // size of content vector + // mork_u1 mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector + +public: + mork_u1 mMaxBookAtom_Body[ morkBookAtom_kMaxBodySize + 3 ]; // max bytes + +public: // empty construction does nothing + morkMaxBookAtom() { } + void InitMaxBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid) + { this->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); } + +private: // copying is not allowed + morkMaxBookAtom(const morkMaxBookAtom& other); + morkMaxBookAtom& operator=(const morkMaxBookAtom& other); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOM_ */ + diff --git a/db/mork/src/morkAtomMap.cpp b/db/mork/src/morkAtomMap.cpp new file mode 100644 index 0000000000..2d8658402b --- /dev/null +++ b/db/mork/src/morkAtomMap.cpp @@ -0,0 +1,427 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKATOMMAP_ +#include "morkAtomMap.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKINTMAP_ +#include "morkIntMap.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkAtomAidMap::CloseMorkNode(morkEnv* ev) // CloseAtomAidMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseAtomAidMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomAidMap::~morkAtomAidMap() // assert CloseAtomAidMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + + +/*public non-poly*/ +morkAtomAidMap::morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +#ifdef MORK_ENABLE_PROBE_MAPS +: morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + ioSlotHeap, morkAtomAidMap_kStartSlotCount, + /*inZeroIsClearKey*/ morkBool_kTrue) +#else /*MORK_ENABLE_PROBE_MAPS*/ +: morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + morkAtomAidMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +#endif /*MORK_ENABLE_PROBE_MAPS*/ +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kAtomAidMap; +} + +/*public non-poly*/ void +morkAtomAidMap::CloseAtomAidMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { +#ifdef MORK_ENABLE_PROBE_MAPS + this->CloseProbeMap(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->CloseMap(ev); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +#ifdef MORK_ENABLE_PROBE_MAPS + + /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) + morkAtomAidMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const + { + MORK_USED_1(ev); + const morkBookAtom* key = *(const morkBookAtom**) inMapKey; + if ( key ) + { + mork_bool hit = key->EqualAid(*(const morkBookAtom**) inAppKey); + return ( hit ) ? morkTest_kHit : morkTest_kMiss; + } + else + return morkTest_kVoid; + } + + /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) + morkAtomAidMap::MapHash(morkEnv* ev, const void* inAppKey) const + { + const morkBookAtom* key = *(const morkBookAtom**) inAppKey; + if ( key ) + return key->HashAid(); + else + { + ev->NilPointerWarning(); + return 0; + } + } + + /*virtual*/ mork_u4 + morkAtomAidMap::ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const + { + const morkBookAtom* key = *(const morkBookAtom**) inMapKey; + if ( key ) + return key->HashAid(); + else + { + ev->NilPointerWarning(); + return 0; + } + } +#else /*MORK_ENABLE_PROBE_MAPS*/ + // { ===== begin morkMap poly interface ===== + /*virtual*/ mork_bool // + morkAtomAidMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const + { + MORK_USED_1(ev); + return (*(const morkBookAtom**) inKeyA)->EqualAid( + *(const morkBookAtom**) inKeyB); + } + + /*virtual*/ mork_u4 // + morkAtomAidMap::Hash(morkEnv* ev, const void* inKey) const + { + MORK_USED_1(ev); + return (*(const morkBookAtom**) inKey)->HashAid(); + } + // } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + +mork_bool +morkAtomAidMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom) +{ + if ( ev->Good() ) + { +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Put(ev, &ioAtom, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } + return ev->Good(); +} + +morkBookAtom* +morkAtomAidMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom) +{ + morkBookAtom* oldKey = 0; + +#ifdef MORK_ENABLE_PROBE_MAPS + MORK_USED_1(inAtom); + morkProbeMap::ProbeMapCutError(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0, + (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +morkBookAtom* +morkAtomAidMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom) +{ + morkBookAtom* key = 0; // old val in the map + +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return key; +} + +morkBookAtom* +morkAtomAidMap::GetAid(morkEnv* ev, mork_aid inAid) +{ + morkWeeBookAtom weeAtom(inAid); + morkBookAtom* key = &weeAtom; // we need a pointer + morkBookAtom* oldKey = 0; // old key in the map + +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkAtomBodyMap::CloseMorkNode(morkEnv* ev) // CloseAtomBodyMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseAtomBodyMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomBodyMap::~morkAtomBodyMap() // assert CloseAtomBodyMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + + +/*public non-poly*/ +morkAtomBodyMap::morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +#ifdef MORK_ENABLE_PROBE_MAPS +: morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + ioSlotHeap, morkAtomBodyMap_kStartSlotCount, + /*inZeroIsClearKey*/ morkBool_kTrue) +#else /*MORK_ENABLE_PROBE_MAPS*/ +: morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0, + morkAtomBodyMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +#endif /*MORK_ENABLE_PROBE_MAPS*/ +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kAtomBodyMap; +} + +/*public non-poly*/ void +morkAtomBodyMap::CloseAtomBodyMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { +#ifdef MORK_ENABLE_PROBE_MAPS + this->CloseProbeMap(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->CloseMap(ev); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` +#ifdef MORK_ENABLE_PROBE_MAPS + + /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) + morkAtomBodyMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const + { + const morkBookAtom* key = *(const morkBookAtom**) inMapKey; + if ( key ) + { + return ( key->EqualFormAndBody(ev, *(const morkBookAtom**) inAppKey) ) ? + morkTest_kHit : morkTest_kMiss; + } + else + return morkTest_kVoid; + } + + /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) + morkAtomBodyMap::MapHash(morkEnv* ev, const void* inAppKey) const + { + const morkBookAtom* key = *(const morkBookAtom**) inAppKey; + if ( key ) + return key->HashFormAndBody(ev); + else + return 0; + } + + /*virtual*/ mork_u4 + morkAtomBodyMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const + { + const morkBookAtom* key = *(const morkBookAtom**) inMapKey; + if ( key ) + return key->HashFormAndBody(ev); + else + return 0; + } +#else /*MORK_ENABLE_PROBE_MAPS*/ + // { ===== begin morkMap poly interface ===== + /*virtual*/ mork_bool // + morkAtomBodyMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const + { + return (*(const morkBookAtom**) inKeyA)->EqualFormAndBody(ev, + *(const morkBookAtom**) inKeyB); + } + + /*virtual*/ mork_u4 // + morkAtomBodyMap::Hash(morkEnv* ev, const void* inKey) const + { + return (*(const morkBookAtom**) inKey)->HashFormAndBody(ev); + } + // } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + +mork_bool +morkAtomBodyMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom) +{ + if ( ev->Good() ) + { +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Put(ev, &ioAtom, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } + return ev->Good(); +} + +morkBookAtom* +morkAtomBodyMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom) +{ + morkBookAtom* oldKey = 0; + +#ifdef MORK_ENABLE_PROBE_MAPS + MORK_USED_1(inAtom); + morkProbeMap::ProbeMapCutError(ev); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0, + (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return oldKey; +} + +morkBookAtom* +morkAtomBodyMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom) +{ + morkBookAtom* key = 0; // old val in the map +#ifdef MORK_ENABLE_PROBE_MAPS + this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0); +#else /*MORK_ENABLE_PROBE_MAPS*/ + this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + return key; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkAtomRowMap::~morkAtomRowMap() +{ +} + +// I changed to sizeof(mork_ip) from sizeof(mork_aid) to fix a crash on +// 64 bit machines. I am not sure it was the right way to fix the problem, +// but it does stop the crash. Perhaps we should be using the +// morkPointerMap instead? +morkAtomRowMap::morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn) + : morkIntMap(ev, inUsage, sizeof(mork_ip), ioHeap, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +, mAtomRowMap_IndexColumn( inIndexColumn ) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kAtomRowMap; +} + +void morkAtomRowMap::AddRow(morkEnv* ev, morkRow* ioRow) +// add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. +{ + mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn); + if ( aid ) + this->AddAid(ev, aid, ioRow); +} + +void morkAtomRowMap::CutRow(morkEnv* ev, morkRow* ioRow) +// cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. +{ + mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn); + if ( aid ) + this->CutAid(ev, aid); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkAtomMap.h b/db/mork/src/morkAtomMap.h new file mode 100644 index 0000000000..6ddecc5c21 --- /dev/null +++ b/db/mork/src/morkAtomMap.h @@ -0,0 +1,365 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKATOMMAP_ +#define _MORKATOMMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +#include "morkProbeMap.h" +#endif + +#ifndef _MORKINTMAP_ +#include "morkIntMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomAidMap /*i*/ 0x6141 /* ascii 'aA' */ + +#define morkAtomAidMap_kStartSlotCount 23 + +/*| morkAtomAidMap: keys of morkBookAtom organized by atom ID +|*/ +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomAidMap : public morkProbeMap { // for mapping tokens to maps +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomAidMap : public morkMap { // for mapping tokens to maps +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomAidMap() only if open + virtual ~morkAtomAidMap(); // assert that CloseAtomAidMap() executed earlier + +public: // morkMap construction & destruction + morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseAtomAidMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsAtomAidMap() const + { return IsNode() && mNode_Derived == morkDerived_kAtomAidMap; } +// } ===== end morkNode methods ===== + +public: +#ifdef MORK_ENABLE_PROBE_MAPS + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== +#else /*MORK_ENABLE_PROBE_MAPS*/ +// { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkBookAtom::HashAid() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkBookAtom::EqualAid() +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +public: // other map methods + + mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom); + // AddAtom() returns ev->Good() + + morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom); + // CutAtom() returns the atom removed equal to inAtom, if there was one + + morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom); + // GetAtom() returns the atom equal to inAtom, or else nil + + morkBookAtom* GetAid(morkEnv* ev, mork_aid inAid); + // GetAid() returns the atom equal to inAid, or else nil + + // note the atoms are owned elsewhere, usuall by morkAtomSpace + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomAidMap(morkAtomAidMap* me, + morkEnv* ev, morkAtomAidMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongAtomAidMap(morkAtomAidMap* me, + morkEnv* ev, morkAtomAidMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomAidMapIter: public morkProbeMapIter { // typesafe wrapper class +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomAidMapIter: public morkMapIter { // typesafe wrapper class +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +public: +#ifdef MORK_ENABLE_PROBE_MAPS + morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) + : morkProbeMapIter(ev, ioMap) { } + + morkAtomAidMapIter( ) : morkProbeMapIter() { } +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkAtomAidMapIter( ) : morkMapIter() { } +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + void InitAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomBodyMap /*i*/ 0x6142 /* ascii 'aB' */ + +#define morkAtomBodyMap_kStartSlotCount 23 + +/*| morkAtomBodyMap: keys of morkBookAtom organized by body bytes +|*/ +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomBodyMap : public morkProbeMap { // for mapping tokens to maps +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomBodyMap : public morkMap { // for mapping tokens to maps +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomBodyMap() only if open + virtual ~morkAtomBodyMap(); // assert CloseAtomBodyMap() executed earlier + +public: // morkMap construction & destruction + morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseAtomBodyMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsAtomBodyMap() const + { return IsNode() && mNode_Derived == morkDerived_kAtomBodyMap; } +// } ===== end morkNode methods ===== + +public: +#ifdef MORK_ENABLE_PROBE_MAPS + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== +#else /*MORK_ENABLE_PROBE_MAPS*/ +// { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkBookAtom::EqualFormAndBody() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkBookAtom::HashFormAndBody() +// } ===== end morkMap poly interface ===== +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +public: // other map methods + + mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom); + // AddAtom() returns ev->Good() + + morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom); + // CutAtom() returns the atom removed equal to inAtom, if there was one + + morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom); + // GetAtom() returns the atom equal to inAtom, or else nil + + // note the atoms are owned elsewhere, usuall by morkAtomSpace + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomBodyMap(morkAtomBodyMap* me, + morkEnv* ev, morkAtomBodyMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongAtomBodyMap(morkAtomBodyMap* me, + morkEnv* ev, morkAtomBodyMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +#ifdef MORK_ENABLE_PROBE_MAPS +class morkAtomBodyMapIter: public morkProbeMapIter{ // typesafe wrapper class +#else /*MORK_ENABLE_PROBE_MAPS*/ +class morkAtomBodyMapIter: public morkMapIter{ // typesafe wrapper class +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +public: +#ifdef MORK_ENABLE_PROBE_MAPS + morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) + : morkProbeMapIter(ev, ioMap) { } + + morkAtomBodyMapIter( ) : morkProbeMapIter() { } +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkAtomBodyMapIter( ) : morkMapIter() { } +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + void InitAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); } + + mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr) + { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomRowMap /*i*/ 0x6152 /* ascii 'aR' */ + +/*| morkAtomRowMap: maps morkAtom* -> morkRow* +|*/ +class morkAtomRowMap : public morkIntMap { // for mapping atoms to rows + +public: + mork_column mAtomRowMap_IndexColumn; // row column being indexed + +public: + + virtual ~morkAtomRowMap(); + morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn); + +public: // adding and cutting from morkRow instance candidate + + void AddRow(morkEnv* ev, morkRow* ioRow); + // add ioRow only if it contains a cell in mAtomRowMap_IndexColumn. + + void CutRow(morkEnv* ev, morkRow* ioRow); + // cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn. + +public: // other map methods + + mork_bool AddAid(morkEnv* ev, mork_aid inAid, morkRow* ioRow) + { return this->AddInt(ev, inAid, ioRow); } + // the AddAid() boolean return equals ev->Good(). + + mork_bool CutAid(morkEnv* ev, mork_aid inAid) + { return this->CutInt(ev, inAid); } + // The CutAid() boolean return indicates whether removal happened. + + morkRow* GetAid(morkEnv* ev, mork_aid inAid) + { return (morkRow*) this->GetInt(ev, inAid); } + // Note the returned space does NOT have an increase in refcount for this. + +public: // dynamic type identification + mork_bool IsAtomRowMap() const + { return IsNode() && mNode_Derived == morkDerived_kAtomRowMap; } + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomRowMap(morkAtomRowMap* me, + morkEnv* ev, morkAtomRowMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongAtomRowMap(morkAtomRowMap* me, + morkEnv* ev, morkAtomRowMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } + +}; + +class morkAtomRowMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkAtomRowMapIter( ) : morkMapIter() { } + void InitAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* + FirstAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow) + { return this->First(ev, outAtom, outRow); } + + mork_change* + NextAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow) + { return this->Next(ev, outAtom, outRow); } + + mork_change* + HereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow) + { return this->Here(ev, outAtom, outRow); } + + mork_change* + CutHereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow) + { return this->CutHere(ev, outAtom, outRow); } +}; + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOMMAP_ */ diff --git a/db/mork/src/morkAtomSpace.cpp b/db/mork/src/morkAtomSpace.cpp new file mode 100644 index 0000000000..e9c1c16ed8 --- /dev/null +++ b/db/mork/src/morkAtomSpace.cpp @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkAtomSpace::CloseMorkNode(morkEnv* ev) // CloseAtomSpace() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseAtomSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkAtomSpace::~morkAtomSpace() // assert CloseAtomSpace() executed earlier +{ + MORK_ASSERT(mAtomSpace_HighUnderId==0); + MORK_ASSERT(mAtomSpace_HighOverId==0); + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mAtomSpace_AtomAids.IsShutNode()); + MORK_ASSERT(mAtomSpace_AtomBodies.IsShutNode()); +} + +/*public non-poly*/ +morkAtomSpace::morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, + mork_scope inScope, morkStore* ioStore, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap) +, mAtomSpace_HighUnderId( morkAtomSpace_kMinUnderId ) +, mAtomSpace_HighOverId( morkAtomSpace_kMinOverId ) +, mAtomSpace_AtomAids(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap) +, mAtomSpace_AtomBodies(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap) +{ + // the morkSpace base constructor handles any dirty propagation + if ( ev->Good() ) + mNode_Derived = morkDerived_kAtomSpace; +} + +/*public non-poly*/ void +morkAtomSpace::CloseAtomSpace(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mAtomSpace_AtomBodies.CloseMorkNode(ev); + morkStore* store = mSpace_Store; + if ( store ) + this->CutAllAtoms(ev, &store->mStore_Pool); + + mAtomSpace_AtomAids.CloseMorkNode(ev); + this->CloseSpace(ev); + mAtomSpace_HighUnderId = 0; + mAtomSpace_HighOverId = 0; + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkAtomSpace::NonAtomSpaceTypeError(morkEnv* ev) +{ + ev->NewError("non morkAtomSpace"); +} + +mork_num +morkAtomSpace::CutAllAtoms(morkEnv* ev, morkPool* ioPool) +{ +#ifdef MORK_ENABLE_ZONE_ARENAS + MORK_USED_2(ev, ioPool); + return 0; +#else /*MORK_ENABLE_ZONE_ARENAS*/ + if ( this->IsAtomSpaceClean() ) + this->MaybeDirtyStoreAndSpace(); + + mork_num outSlots = mAtomSpace_AtomAids.MapFill(); + morkBookAtom* a = 0; // old key atom in the map + + morkStore* store = mSpace_Store; + mork_change* c = 0; + morkAtomAidMapIter i(ev, &mAtomSpace_AtomAids); + for ( c = i.FirstAtom(ev, &a); c ; c = i.NextAtom(ev, &a) ) + { + if ( a ) + ioPool->ZapAtom(ev, a, &store->mStore_Zone); + +#ifdef MORK_ENABLE_PROBE_MAPS + // do not cut anything from the map +#else /*MORK_ENABLE_PROBE_MAPS*/ + i.CutHereAtom(ev, /*key*/ (morkBookAtom**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } + + return outSlots; +#endif /*MORK_ENABLE_ZONE_ARENAS*/ +} + + +morkBookAtom* +morkAtomSpace::MakeBookAtomCopyWithAid(morkEnv* ev, + const morkFarBookAtom& inAtom, mork_aid inAid) +// Make copy of inAtom and put it in both maps, using specified ID. +{ + morkBookAtom* outAtom = 0; + morkStore* store = mSpace_Store; + if ( ev->Good() && store ) + { + morkPool* pool = this->GetSpaceStorePool(); + outAtom = pool->NewFarBookAtomCopy(ev, inAtom, &store->mStore_Zone); + if ( outAtom ) + { + if ( store->mStore_CanDirty ) + { + outAtom->SetAtomDirty(); + if ( this->IsAtomSpaceClean() ) + this->MaybeDirtyStoreAndSpace(); + } + + outAtom->mBookAtom_Id = inAid; + outAtom->mBookAtom_Space = this; + mAtomSpace_AtomAids.AddAtom(ev, outAtom); + mAtomSpace_AtomBodies.AddAtom(ev, outAtom); + if ( this->SpaceScope() == morkAtomSpace_kColumnScope ) + outAtom->MakeCellUseForever(ev); + + if ( mAtomSpace_HighUnderId <= inAid ) + mAtomSpace_HighUnderId = inAid + 1; + } + } + return outAtom; +} + +morkBookAtom* +morkAtomSpace::MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom) +// make copy of inAtom and put it in both maps, using a new ID as needed. +{ + morkBookAtom* outAtom = 0; + morkStore* store = mSpace_Store; + if ( ev->Good() && store ) + { + if ( store->mStore_CanAutoAssignAtomIdentity ) + { + morkPool* pool = this->GetSpaceStorePool(); + morkBookAtom* atom = pool->NewFarBookAtomCopy(ev, inAtom, &mSpace_Store->mStore_Zone); + if ( atom ) + { + mork_aid id = this->MakeNewAtomId(ev, atom); + if ( id ) + { + if ( store->mStore_CanDirty ) + { + atom->SetAtomDirty(); + if ( this->IsAtomSpaceClean() ) + this->MaybeDirtyStoreAndSpace(); + } + + outAtom = atom; + atom->mBookAtom_Space = this; + mAtomSpace_AtomAids.AddAtom(ev, atom); + mAtomSpace_AtomBodies.AddAtom(ev, atom); + if ( this->SpaceScope() == morkAtomSpace_kColumnScope ) + outAtom->MakeCellUseForever(ev); + } + else + pool->ZapAtom(ev, atom, &mSpace_Store->mStore_Zone); + } + } + else + mSpace_Store->CannotAutoAssignAtomIdentityError(ev); + } + return outAtom; +} + + +mork_aid +morkAtomSpace::MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom) +{ + mork_aid outAid = 0; + mork_tid id = mAtomSpace_HighUnderId; + mork_num count = 8; // try up to eight times + + while ( !outAid && count ) // still trying to find an unused table ID? + { + --count; + ioAtom->mBookAtom_Id = id; + if ( !mAtomSpace_AtomAids.GetAtom(ev, ioAtom) ) + outAid = id; + else + { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + mAtomSpace_HighUnderId = id + 1; + return outAid; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +morkAtomSpaceMap::~morkAtomSpaceMap() +{ +} + +morkAtomSpaceMap::morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kAtomSpaceMap; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkAtomSpace.h b/db/mork/src/morkAtomSpace.h new file mode 100644 index 0000000000..b72a594010 --- /dev/null +++ b/db/mork/src/morkAtomSpace.h @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKATOMSPACE_ +#define _MORKATOMSPACE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKATOMMAP_ +#include "morkAtomMap.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kMinUnderId: the smallest ID we auto-assign to the 'under' namespace +**| reserved for tokens expected to occur very frequently, such as the names +**| of columns. We reserve single byte ids in the ASCII range to correspond +**| one-to-one to those tokens consisting single ASCII characters (so that +**| this assignment is always known and constant). So we start at 0x80, and +**| then reserve the upper half of two hex digit ids and all the three hex +**| digit IDs for the 'under' namespace for common tokens. +|*/ +#define morkAtomSpace_kMinUnderId 0x80 /* low 7 bits mean byte tokens */ + +#define morkAtomSpace_kMaxSevenBitAid 0x7F /* low seven bit integer ID */ + +/*| kMinOverId: the smallest ID we auto-assign to the 'over' namespace that +**| might include very large numbers of tokens that are used infrequently, +**| so that we care less whether the shortest hex representation is used. +**| So we start all IDs for 'over' category tokens at a value range that +**| needs at least four hex digits, so we can reserve three hex digits and +**| shorter for more commonly occuring tokens in the 'under' category. +|*/ +#define morkAtomSpace_kMinOverId 0x1000 /* using at least four hex bytes */ + +#define morkDerived_kAtomSpace /*i*/ 0x6153 /* ascii 'aS' */ + +#define morkAtomSpace_kColumnScope ((mork_scope) 'c') /* column scope is forever */ + +/*| morkAtomSpace: +|*/ +class morkAtomSpace : public morkSpace { // + +// public: // slots inherited from morkSpace (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkStore* mSpace_Store; // weak ref to containing store + + // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment + +public: // state is public because the entire Mork system is private + + mork_aid mAtomSpace_HighUnderId; // high ID in 'under' range + mork_aid mAtomSpace_HighOverId; // high ID in 'over' range + + morkAtomAidMap mAtomSpace_AtomAids; // all atoms in space by ID + morkAtomBodyMap mAtomSpace_AtomBodies; // all atoms in space by body + +public: // more specific dirty methods for atom space: + void SetAtomSpaceDirty() { this->SetNodeDirty(); } + void SetAtomSpaceClean() { this->SetNodeClean(); } + + mork_bool IsAtomSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsAtomSpaceDirty() const { return this->IsNodeDirty(); } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomSpace() only if open + virtual ~morkAtomSpace(); // assert that CloseAtomSpace() executed earlier + +public: // morkMap construction & destruction + morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap); + void CloseAtomSpace(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsAtomSpace() const + { return IsNode() && mNode_Derived == morkDerived_kAtomSpace; } +// } ===== end morkNode methods ===== + +public: // typing + void NonAtomSpaceTypeError(morkEnv* ev); + +public: // setup + + mork_bool MarkAllAtomSpaceContentDirty(morkEnv* ev); + // MarkAllAtomSpaceContentDirty() visits every space object and marks + // them dirty, including every table, row, cell, and atom. The return + // equals ev->Good(), to show whether any error happened. This method is + // intended for use in the beginning of a "compress commit" which writes + // all store content, whether dirty or not. We dirty everything first so + // that later iterations over content can mark things clean as they are + // written, and organize the process of serialization so that objects are + // written only at need (because of being dirty). + +public: // other space methods + + // void ReserveColumnAidCount(mork_count inCount) + // { + // mAtomSpace_HighUnderId = morkAtomSpace_kMinUnderId + inCount; + // mAtomSpace_HighOverId = morkAtomSpace_kMinOverId + inCount; + // } + + mork_num CutAllAtoms(morkEnv* ev, morkPool* ioPool); + // CutAllAtoms() puts all the atoms back in the pool. + + morkBookAtom* MakeBookAtomCopyWithAid(morkEnv* ev, + const morkFarBookAtom& inAtom, mork_aid inAid); + // Make copy of inAtom and put it in both maps, using specified ID. + + morkBookAtom* MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom); + // Make copy of inAtom and put it in both maps, using a new ID as needed. + + mork_aid MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom); + // generate an unused atom id. + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakAtomSpace(morkAtomSpace* me, + morkEnv* ev, morkAtomSpace** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongAtomSpace(morkAtomSpace* me, + morkEnv* ev, morkAtomSpace** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kAtomSpaceMap /*i*/ 0x615A /* ascii 'aZ' */ + +/*| morkAtomSpaceMap: maps mork_scope -> morkAtomSpace +|*/ +class morkAtomSpaceMap : public morkNodeMap { // for mapping tokens to tables + +public: + + virtual ~morkAtomSpaceMap(); + morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + +public: // other map methods + + mork_bool AddAtomSpace(morkEnv* ev, morkAtomSpace* ioAtomSpace) + { return this->AddNode(ev, ioAtomSpace->SpaceScope(), ioAtomSpace); } + // the AddAtomSpace() boolean return equals ev->Good(). + + mork_bool CutAtomSpace(morkEnv* ev, mork_scope inScope) + { return this->CutNode(ev, inScope); } + // The CutAtomSpace() boolean return indicates whether removal happened. + + morkAtomSpace* GetAtomSpace(morkEnv* ev, mork_scope inScope) + { return (morkAtomSpace*) this->GetNode(ev, inScope); } + // Note the returned space does NOT have an increase in refcount for this. + + mork_num CutAllAtomSpaces(morkEnv* ev) + { return this->CutAllNodes(ev); } + // CutAllAtomSpaces() releases all the referenced table values. +}; + +class morkAtomSpaceMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkAtomSpaceMapIter( ) : morkMapIter() { } + void InitAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* + FirstAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace) + { return this->First(ev, outScope, outAtomSpace); } + + mork_change* + NextAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace) + { return this->Next(ev, outScope, outAtomSpace); } + + mork_change* + HereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace) + { return this->Here(ev, outScope, outAtomSpace); } + + mork_change* + CutHereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace) + { return this->CutHere(ev, outScope, outAtomSpace); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKATOMSPACE_ */ + diff --git a/db/mork/src/morkBead.cpp b/db/mork/src/morkBead.cpp new file mode 100644 index 0000000000..2b552eff13 --- /dev/null +++ b/db/mork/src/morkBead.cpp @@ -0,0 +1,425 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKBEAD_ +#include "morkBead.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkBead::CloseMorkNode(morkEnv* ev) // CloseBead() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseBead(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBead::~morkBead() // assert CloseBead() executed earlier +{ + MORK_ASSERT(mBead_Color==0 || mNode_Usage == morkUsage_kStack ); +} + +/*public non-poly*/ +morkBead::morkBead(mork_color inBeadColor) +: morkNode( morkUsage_kStack ) +, mBead_Color( inBeadColor ) +{ +} + +/*public non-poly*/ +morkBead::morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor) +: morkNode( inUsage, ioHeap ) +, mBead_Color( inBeadColor ) +{ +} + +/*public non-poly*/ +morkBead::morkBead(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, mork_color inBeadColor) +: morkNode(ev, inUsage, ioHeap) +, mBead_Color( inBeadColor ) +{ + if ( ev->Good() ) + { + if ( ev->Good() ) + mNode_Derived = morkDerived_kBead; + } +} + +/*public non-poly*/ void +morkBead::CloseBead(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + if ( !this->IsShutNode() ) + { + mBead_Color = 0; + this->MarkShut(); + } + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkBeadMap::CloseMorkNode(morkEnv* ev) // CloseBeadMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseBeadMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBeadMap::~morkBeadMap() // assert CloseBeadMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkBeadMap::morkBeadMap(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkMap(ev, inUsage, ioHeap, sizeof(morkBead*), /*inValSize*/ 0, + /*slotCount*/ 11, ioSlotHeap, /*holdChanges*/ morkBool_kFalse) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kBeadMap; +} + +/*public non-poly*/ void +morkBeadMap::CloseBeadMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CutAllBeads(ev); + this->CloseMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool +morkBeadMap::AddBead(morkEnv* ev, morkBead* ioBead) + // the AddBead() boolean return equals ev->Good(). +{ + if ( ioBead && ev->Good() ) + { + morkBead* oldBead = 0; // old key in the map + + mork_bool put = this->Put(ev, &ioBead, /*val*/ (void*) 0, + /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0); + + if ( put ) // replaced an existing key? + { + if ( oldBead != ioBead ) // new bead was not already in table? + ioBead->AddStrongRef(ev); // now there's another ref + + if ( oldBead && oldBead != ioBead ) // need to release old node? + oldBead->CutStrongRef(ev); + } + else + ioBead->AddStrongRef(ev); // another ref if not already in table + } + else if ( !ioBead ) + ev->NilPointerError(); + + return ev->Good(); +} + +mork_bool +morkBeadMap::CutBead(morkEnv* ev, mork_color inColor) +{ + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + mork_bool outCutNode = this->Cut(ev, &key, + /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0); + + if ( oldBead ) + oldBead->CutStrongRef(ev); + + bead.CloseBead(ev); + return outCutNode; +} + +morkBead* +morkBeadMap::GetBead(morkEnv* ev, mork_color inColor) + // Note the returned bead does NOT have an increase in refcount for this. +{ + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + this->Get(ev, &key, /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0); + + bead.CloseBead(ev); + return oldBead; +} + +mork_num +morkBeadMap::CutAllBeads(morkEnv* ev) + // CutAllBeads() releases all the referenced beads. +{ + mork_num outSlots = mMap_Slots; + + morkBeadMapIter i(ev, this); + morkBead* b = i.FirstBead(ev); + + while ( b ) + { + b->CutStrongRef(ev); + i.CutHereBead(ev); + b = i.NextBead(ev); + } + + return outSlots; +} + + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool +morkBeadMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const +{ + MORK_USED_1(ev); + return (*(const morkBead**) inKeyA)->BeadEqual( + *(const morkBead**) inKeyB); +} + +/*virtual*/ mork_u4 +morkBeadMap::Hash(morkEnv* ev, const void* inKey) const +{ + MORK_USED_1(ev); + return (*(const morkBead**) inKey)->BeadHash(); +} +// } ===== end morkMap poly interface ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +morkBead* morkBeadMapIter::FirstBead(morkEnv* ev) +{ + morkBead* bead = 0; + this->First(ev, &bead, /*val*/ (void*) 0); + return bead; +} + +morkBead* morkBeadMapIter::NextBead(morkEnv* ev) +{ + morkBead* bead = 0; + this->Next(ev, &bead, /*val*/ (void*) 0); + return bead; +} + +morkBead* morkBeadMapIter::HereBead(morkEnv* ev) +{ + morkBead* bead = 0; + this->Here(ev, &bead, /*val*/ (void*) 0); + return bead; +} + +void morkBeadMapIter::CutHereBead(morkEnv* ev) +{ + this->CutHere(ev, /*key*/ (void*) 0, /*val*/ (void*) 0); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkBeadProbeMap::CloseMorkNode(morkEnv* ev) // CloseBeadProbeMap() if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseBeadProbeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBeadProbeMap::~morkBeadProbeMap() // assert CloseBeadProbeMap() earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + + +/*public non-poly*/ +morkBeadProbeMap::morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkBead*), /*inValSize*/ 0, + ioSlotHeap, /*startSlotCount*/ 11, + /*inZeroIsClearKey*/ morkBool_kTrue) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kBeadProbeMap; +} + +/*public non-poly*/ void +morkBeadProbeMap::CloseBeadProbeMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CutAllBeads(ev); + this->CloseProbeMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkBeadProbeMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const +{ + MORK_USED_1(ev); + const morkBead* key = *(const morkBead**) inMapKey; + if ( key ) + { + mork_bool hit = key->BeadEqual(*(const morkBead**) inAppKey); + return ( hit ) ? morkTest_kHit : morkTest_kMiss; + } + else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkBeadProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const +{ + const morkBead* key = *(const morkBead**) inAppKey; + if ( key ) + return key->BeadHash(); + else + { + ev->NilPointerWarning(); + return 0; + } +} + +/*virtual*/ mork_u4 +morkBeadProbeMap::ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const +{ + const morkBead* key = *(const morkBead**) inMapKey; + if ( key ) + return key->BeadHash(); + else + { + ev->NilPointerWarning(); + return 0; + } +} + +mork_bool +morkBeadProbeMap::AddBead(morkEnv* ev, morkBead* ioBead) +{ + if ( ioBead && ev->Good() ) + { + morkBead* bead = 0; // old key in the map + + mork_bool put = this->MapAtPut(ev, &ioBead, /*val*/ (void*) 0, + /*key*/ &bead, /*val*/ (void*) 0); + + if ( put ) // replaced an existing key? + { + if ( bead != ioBead ) // new bead was not already in table? + ioBead->AddStrongRef(ev); // now there's another ref + + if ( bead && bead != ioBead ) // need to release old node? + bead->CutStrongRef(ev); + } + else + ioBead->AddStrongRef(ev); // now there's another ref + } + else if ( !ioBead ) + ev->NilPointerError(); + + return ev->Good(); +} + +morkBead* +morkBeadProbeMap::GetBead(morkEnv* ev, mork_color inColor) +{ + morkBead* oldBead = 0; // old key in the map + morkBead bead(inColor); + morkBead* key = &bead; + + this->MapAt(ev, &key, &oldBead, /*val*/ (void*) 0); + + bead.CloseBead(ev); + return oldBead; +} + +mork_num +morkBeadProbeMap::CutAllBeads(morkEnv* ev) + // CutAllBeads() releases all the referenced bead values. +{ + mork_num outSlots = sMap_Slots; + + morkBeadProbeMapIter i(ev, this); + morkBead* b = i.FirstBead(ev); + + while ( b ) + { + b->CutStrongRef(ev); + b = i.NextBead(ev); + } + this->MapCutAll(ev); + + return outSlots; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + diff --git a/db/mork/src/morkBead.h b/db/mork/src/morkBead.h new file mode 100644 index 0000000000..83ceef2f41 --- /dev/null +++ b/db/mork/src/morkBead.h @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKBEAD_ +#define _MORKBEAD_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +#include "morkProbeMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBead /*i*/ 0x426F /* ascii 'Bo' */ + +/*| morkBead: subclass of morkNode that adds knowledge of db suite factory +**| and containing port to those objects that are exposed as instances of +**| nsIMdbBead in the public interface. +|*/ +class morkBead : public morkNode { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + + mork_color mBead_Color; // ID for this bead + +public: // Hash() and Equal() for bead maps are same for all subclasses: + + mork_u4 BeadHash() const { return (mork_u4) mBead_Color; } + mork_bool BeadEqual(const morkBead* inBead) const + { return ( mBead_Color == inBead->mBead_Color); } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseBead() only if open + virtual ~morkBead(); // assert that CloseBead() executed earlier + +public: // special case for stack construction for map usage: + morkBead(mork_color inBeadColor); // stack-based bead instance + +protected: // special case for morkObject: + morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + +public: // morkEnv construction & destruction + morkBead(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + void CloseBead(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkBead(const morkBead& other); + morkBead& operator=(const morkBead& other); + +public: // dynamic type identification + mork_bool IsBead() const + { return IsNode() && mNode_Derived == morkDerived_kBead; } +// } ===== end morkNode methods ===== + + // void NewNilHandleError(morkEnv* ev); // mBead_Handle is nil + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakBead(morkBead* me, + morkEnv* ev, morkBead** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongBead(morkBead* me, + morkEnv* ev, morkBead** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBeadMap /*i*/ 0x744D /* ascii 'bM' */ + +/*| morkBeadMap: maps bead -> bead (key only using mBead_Color) +|*/ +class morkBeadMap : public morkMap { + + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseBeadMap() only if open + virtual ~morkBeadMap(); // assert that CloseBeadMap() executed earlier + +public: // morkMap construction & destruction + morkBeadMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseBeadMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsBeadMap() const + { return IsNode() && mNode_Derived == morkDerived_kBeadMap; } +// } ===== end morkNode methods ===== + +// { ===== begin morkMap poly interface ===== +public: + virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const override; +// } ===== end morkMap poly interface ===== + +public: // other map methods + + mork_bool AddBead(morkEnv* ev, morkBead* ioBead); + // the AddBead() boolean return equals ev->Good(). + + mork_bool CutBead(morkEnv* ev, mork_color inColor); + // The CutBead() boolean return indicates whether removal happened. + + morkBead* GetBead(morkEnv* ev, mork_color inColor); + // Note the returned bead does NOT have an increase in refcount for this. + + mork_num CutAllBeads(morkEnv* ev); + // CutAllBeads() releases all the referenced beads. +}; + +class morkBeadMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkBeadMapIter(morkEnv* ev, morkBeadMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkBeadMapIter( ) : morkMapIter() { } + void InitBeadMapIter(morkEnv* ev, morkBeadMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + morkBead* FirstBead(morkEnv* ev); + morkBead* NextBead(morkEnv* ev); + morkBead* HereBead(morkEnv* ev); + void CutHereBead(morkEnv* ev); + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kBeadProbeMap /*i*/ 0x6D74 /* ascii 'mb' */ + +/*| morkBeadProbeMap: maps bead -> bead (key only using mBead_Color) +|*/ +class morkBeadProbeMap : public morkProbeMap { + + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseBeadProbeMap() only if open + virtual ~morkBeadProbeMap(); // assert that CloseBeadProbeMap() executed earlier + +public: // morkMap construction & destruction + morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseBeadProbeMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsBeadProbeMap() const + { return IsNode() && mNode_Derived == morkDerived_kBeadProbeMap; } +// } ===== end morkNode methods ===== + + // { ===== begin morkProbeMap methods ===== +public: + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== + +public: // other map methods + + mork_bool AddBead(morkEnv* ev, morkBead* ioBead); + // the AddBead() boolean return equals ev->Good(). + + morkBead* GetBead(morkEnv* ev, mork_color inColor); + // Note the returned bead does NOT have an increase in refcount for this. + + mork_num CutAllBeads(morkEnv* ev); + // CutAllBeads() releases all the referenced bead values. +}; + +class morkBeadProbeMapIter: public morkProbeMapIter { // typesafe wrapper class + +public: + morkBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap) + : morkProbeMapIter(ev, ioMap) { } + + morkBeadProbeMapIter( ) : morkProbeMapIter() { } + void InitBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap) + { this->InitProbeMapIter(ev, ioMap); } + + morkBead* FirstBead(morkEnv* ev) + { return (morkBead*) this->IterFirstKey(ev); } + + morkBead* NextBead(morkEnv* ev) + { return (morkBead*) this->IterNextKey(ev); } + + morkBead* HereBead(morkEnv* ev) + { return (morkBead*) this->IterHereKey(ev); } + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBEAD_ */ diff --git a/db/mork/src/morkBlob.cpp b/db/mork/src/morkBlob.cpp new file mode 100644 index 0000000000..0d3f3cccd8 --- /dev/null +++ b/db/mork/src/morkBlob.cpp @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void +morkBuf::NilBufBodyError(morkEnv* ev) +{ + ev->NewError("nil mBuf_Body"); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void +morkBlob::BlobFillOverSizeError(morkEnv* ev) +{ + ev->NewError("mBuf_Fill > mBlob_Size"); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +mork_bool +morkBlob::GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, mork_size inNewSize) +{ + if ( ioHeap ) + { + if ( !mBuf_Body ) // no body? implies zero sized? + mBlob_Size = 0; + + if ( mBuf_Fill > mBlob_Size ) // fill more than size? + { + ev->NewWarning("mBuf_Fill > mBlob_Size"); + mBuf_Fill = mBlob_Size; + } + + if ( inNewSize > mBlob_Size ) // need to allocate larger blob? + { + mork_u1* body = 0; + ioHeap->Alloc(ev->AsMdbEnv(), inNewSize, (void**) &body); + if ( body && ev->Good() ) + { + void* oldBody = mBuf_Body; + if ( mBlob_Size ) // any old content to transfer? + MORK_MEMCPY(body, oldBody, mBlob_Size); + + mBlob_Size = inNewSize; // install new size + mBuf_Body = body; // install new body + + if ( oldBody ) // need to free old buffer body? + ioHeap->Free(ev->AsMdbEnv(), oldBody); + } + } + } + else + ev->NilPointerError(); + + if ( ev->Good() && mBlob_Size < inNewSize ) + ev->NewError("mBlob_Size < inNewSize"); + + return ev->Good(); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkCoil::morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap) +{ + mBuf_Body = 0; + mBuf_Fill = 0; + mBlob_Size = 0; + mText_Form = 0; + mCoil_Heap = ioHeap; + if ( !ioHeap ) + ev->NilPointerError(); +} + +void +morkCoil::CloseCoil(morkEnv* ev) +{ + void* body = mBuf_Body; + nsIMdbHeap* heap = mCoil_Heap; + + mBuf_Body = 0; + mCoil_Heap = 0; + + if ( body && heap ) + { + heap->Free(ev->AsMdbEnv(), body); + } +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkBlob.h b/db/mork/src/morkBlob.h new file mode 100644 index 0000000000..5d07098e1b --- /dev/null +++ b/db/mork/src/morkBlob.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKBLOB_ +#define _MORKBLOB_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| Buf: the minimum needed to describe location and content length. +**| This is typically only enough to read from this buffer, since +**| one cannot write effectively without knowing the size of a buf. +|*/ +class morkBuf { // subset of nsIMdbYarn slots +public: + void* mBuf_Body; // space for holding any binary content + mork_fill mBuf_Fill; // logical content in Buf in bytes + +public: + morkBuf() { } + morkBuf(const void* ioBuf, mork_fill inFill) + : mBuf_Body((void*) ioBuf), mBuf_Fill(inFill) { } + + void ClearBufFill() { mBuf_Fill = 0; } + + static void NilBufBodyError(morkEnv* ev); + +private: // copying is not allowed + morkBuf(const morkBuf& other); + morkBuf& operator=(const morkBuf& other); +}; + +/*| Blob: a buffer with an associated size, to increase known buf info +**| to include max capacity in addition to buf location and content. +**| This form factor allows us to allocate a vector of such blobs, +**| which can share the same managing heap stored elsewhere, and that +**| is why we don't include a pointer to a heap in this blob class. +|*/ +class morkBlob : public morkBuf { // greater subset of nsIMdbYarn slots + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes +public: + mork_size mBlob_Size; // physical size of Buf in bytes + +public: + morkBlob() { } + morkBlob(const void* ioBuf, mork_fill inFill, mork_size inSize) + : morkBuf(ioBuf, inFill), mBlob_Size(inSize) { } + + static void BlobFillOverSizeError(morkEnv* ev); + +public: + mork_bool GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, + mork_size inNewSize); + +private: // copying is not allowed + morkBlob(const morkBlob& other); + morkBlob& operator=(const morkBlob& other); + +}; + +/*| Text: a blob with an associated charset annotation, where the +**| charset actually includes the general notion of typing, and not +**| just a specification of character set alone; we want to permit +**| arbitrary charset annotations for ad hoc binary types as well. +**| (We avoid including a nsIMdbHeap pointer in morkText for the same +**| reason morkBlob does: we want minimal size vectors of morkText.) +|*/ +class morkText : public morkBlob { // greater subset of nsIMdbYarn slots + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes + // mdb_size mBlob_Size; // physical size of Buf in bytes + +public: + mork_cscode mText_Form; // charset format encoding + + morkText() { } + +private: // copying is not allowed + morkText(const morkText& other); + morkText& operator=(const morkText& other); +}; + +/*| Coil: a text with an associated nsIMdbHeap instance that provides +**| all memory management for the space pointed to by mBuf_Body. (This +**| was the hardest type to give a name in this small class hierarchy, +**| because it's hard to characterize self-management of one's space.) +**| A coil is a self-contained blob that knows how to grow itself as +**| necessary to hold more content when necessary. Coil descends from +**| morkText to include the mText_Form slot, even though this won't be +**| needed always, because we are not as concerned about the overall +**| size of this particular Coil object (if we were concerned about +**| the size of an array of Coil instances, we would not bother with +**| a separate heap pointer for each of them). +**| +**|| A coil makes a good medium in which to stream content as a sink, +**| so we will have a subclass of morkSink called morkCoil that +**| will stream bytes into this self-contained coil object. The name +**| of this morkCoil class derives more from this intended usage than +**| from anything else. The Mork code to parse db content will use +**| coils with associated sinks to accumulate parsed strings. +**| +**|| Heap: this is the heap used for memory allocation. This instance +**| is NOT refcounted, since this coil always assumes the heap is held +**| through a reference elsewhere (for example, through the same object +**| that contains or holds the coil itself. This lack of refcounting +**| is consistent with the fact that morkCoil itself is not refcounted, +**| and is not intended for use as a standalone object. +|*/ +class morkCoil : public morkText { // self-managing text blob object + + // void* mBuf_Body; // space for holding any binary content + // mdb_fill mBuf_Fill; // logical content in Buf in bytes + // mdb_size mBlob_Size; // physical size of Buf in bytes + // mdb_cscode mText_Form; // charset format encoding +public: + nsIMdbHeap* mCoil_Heap; // storage manager for mBuf_Body pointer + +public: + morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap); + + void CloseCoil(morkEnv* ev); + + mork_bool GrowCoil(morkEnv* ev, mork_size inNewSize) + { return this->GrowBlob(ev, mCoil_Heap, inNewSize); } + +private: // copying is not allowed + morkCoil(const morkCoil& other); + morkCoil& operator=(const morkCoil& other); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBLOB_ */ diff --git a/db/mork/src/morkBuilder.cpp b/db/mork/src/morkBuilder.cpp new file mode 100644 index 0000000000..27e5bd1988 --- /dev/null +++ b/db/mork/src/morkBuilder.cpp @@ -0,0 +1,1031 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKPARSER_ +#include "morkParser.h" +#endif + +#ifndef _MORKBUILDER_ +#include "morkBuilder.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkBuilder::CloseMorkNode(morkEnv* ev) // CloseBuilder() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseBuilder(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkBuilder::~morkBuilder() // assert CloseBuilder() executed earlier +{ + MORK_ASSERT(mBuilder_Store==0); + MORK_ASSERT(mBuilder_Row==0); + MORK_ASSERT(mBuilder_Table==0); + MORK_ASSERT(mBuilder_Cell==0); + MORK_ASSERT(mBuilder_RowSpace==0); + MORK_ASSERT(mBuilder_AtomSpace==0); +} + +/*public non-poly*/ +morkBuilder::morkBuilder(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, mdb_count inBytesPerParseSegment, + nsIMdbHeap* ioSlotHeap, morkStore* ioStore) + +: morkParser(ev, inUsage, ioHeap, ioStream, + inBytesPerParseSegment, ioSlotHeap) + +, mBuilder_Store( 0 ) + +, mBuilder_Table( 0 ) +, mBuilder_Row( 0 ) +, mBuilder_Cell( 0 ) + +, mBuilder_RowSpace( 0 ) +, mBuilder_AtomSpace( 0 ) + +, mBuilder_OidAtomSpace( 0 ) +, mBuilder_ScopeAtomSpace( 0 ) + +, mBuilder_PortForm( 0 ) +, mBuilder_PortRowScope( (mork_scope) 'r' ) +, mBuilder_PortAtomScope( (mork_scope) 'v' ) + +, mBuilder_TableForm( 0 ) +, mBuilder_TableRowScope( (mork_scope) 'r' ) +, mBuilder_TableAtomScope( (mork_scope) 'v' ) +, mBuilder_TableKind( 0 ) + +, mBuilder_TablePriority( morkPriority_kLo ) +, mBuilder_TableIsUnique( morkBool_kFalse ) +, mBuilder_TableIsVerbose( morkBool_kFalse ) +, mBuilder_TablePadByte( 0 ) + +, mBuilder_RowForm( 0 ) +, mBuilder_RowRowScope( (mork_scope) 'r' ) +, mBuilder_RowAtomScope( (mork_scope) 'v' ) + +, mBuilder_CellForm( 0 ) +, mBuilder_CellAtomScope( (mork_scope) 'v' ) + +, mBuilder_DictForm( 0 ) +, mBuilder_DictAtomScope( (mork_scope) 'v' ) + +, mBuilder_MetaTokenSlot( 0 ) + +, mBuilder_DoCutRow( morkBool_kFalse ) +, mBuilder_DoCutCell( morkBool_kFalse ) +, mBuilder_CellsVecFill( 0 ) +{ + if ( ev->Good() ) + { + if ( ioStore ) + { + morkStore::SlotWeakStore(ioStore, ev, &mBuilder_Store); + if ( ev->Good() ) + mNode_Derived = morkDerived_kBuilder; + } + else + ev->NilPointerError(); + } + +} + +/*public non-poly*/ void +morkBuilder::CloseBuilder(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mBuilder_Row = 0; + mBuilder_Cell = 0; + mBuilder_MetaTokenSlot = 0; + + morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table); + morkStore::SlotWeakStore((morkStore*) 0, ev, &mBuilder_Store); + + morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev, + &mBuilder_RowSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mBuilder_AtomSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mBuilder_OidAtomSpace); + + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mBuilder_ScopeAtomSpace); + this->CloseParser(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkBuilder::NonBuilderTypeError(morkEnv* ev) +{ + ev->NewError("non morkBuilder"); +} + +/*static*/ void +morkBuilder::NilBuilderCellError(morkEnv* ev) +{ + ev->NewError("nil mBuilder_Cell"); +} + +/*static*/ void +morkBuilder::NilBuilderRowError(morkEnv* ev) +{ + ev->NewError("nil mBuilder_Row"); +} + +/*static*/ void +morkBuilder::NilBuilderTableError(morkEnv* ev) +{ + ev->NewError("nil mBuilder_Table"); +} + +/*static*/ void +morkBuilder::NonColumnSpaceScopeError(morkEnv* ev) +{ + ev->NewError("column space != 'c'"); +} + +void +morkBuilder::LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, + const char* inKind) +{ + MORK_USED_2(inGlitch,inKind); + ev->NewWarning("parsing glitch"); +} + +/*virtual*/ void +morkBuilder::MidToYarn(morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) +// The parser might ask that some aliases be turned into yarns, so they +// can be concatenated into longer blobs under some circumstances. This +// is an alternative to using a long and complex callback for many parts +// for a single cell value. +{ + mBuilder_Store->MidToYarn(ev, inMid, outYarn); +} + +/*virtual*/ void +morkBuilder::OnNewPort(morkEnv* ev, const morkPlace& inPlace) +// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd +// mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch +// mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row +{ + MORK_USED_2(ev,inPlace); + // mParser_InPort = morkBool_kTrue; + mBuilder_PortForm = 0; + mBuilder_PortRowScope = (mork_scope) 'r'; + mBuilder_PortAtomScope = (mork_scope) 'v'; +} + +/*virtual*/ void +morkBuilder::OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "port"); +} + +/*virtual*/ void +morkBuilder::OnPortEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd +{ + MORK_USED_2(ev,inSpan); + // ev->StubMethodOnlyError(); + // nothing to do? + // mParser_InPort = morkBool_kFalse; +} + +/*virtual*/ void +morkBuilder::OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) +{ + MORK_USED_1(inPlace); + mParser_InGroup = morkBool_kTrue; + mork_pos startPos = inPlace.mPlace_Pos; + + morkStore* store = mBuilder_Store; + if ( store ) + { + if ( inGid >= store->mStore_CommitGroupIdentity ) + store->mStore_CommitGroupIdentity = inGid + 1; + + if ( !store->mStore_FirstCommitGroupPos ) + store->mStore_FirstCommitGroupPos = startPos; + else if ( !store->mStore_SecondCommitGroupPos ) + store->mStore_SecondCommitGroupPos = startPos; + } +} + +/*virtual*/ void +morkBuilder::OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "group"); +} + +/*virtual*/ void +morkBuilder::OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) +{ + MORK_USED_2(ev,inSpan); + // mParser_InGroup = morkBool_kFalse; + // ev->StubMethodOnlyError(); +} + +/*virtual*/ void +morkBuilder::OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) +{ + MORK_USED_1(inSpan); + // mParser_InGroup = morkBool_kFalse; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void +morkBuilder::OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_change inChange) +{ + MORK_USED_3(inMid,inPlace,inChange); + // mParser_InPortRow = morkBool_kTrue; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void +morkBuilder::OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "port row"); +} + +/*virtual*/ void +morkBuilder::OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) +{ + MORK_USED_1(inSpan); + // mParser_InPortRow = morkBool_kFalse; + ev->StubMethodOnlyError(); +} + +/*virtual*/ void +morkBuilder::OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllRows) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch +// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +// mp:MetaItem ::= mp:Cell | OnMetaGlitch +{ + MORK_USED_1(inPlace); + // mParser_InTable = morkBool_kTrue; + mBuilder_TableForm = mBuilder_PortForm; + mBuilder_TableRowScope = mBuilder_PortRowScope; + mBuilder_TableAtomScope = mBuilder_PortAtomScope; + mBuilder_TableKind = morkStore_kNoneToken; + + mBuilder_TablePriority = morkPriority_kLo; + mBuilder_TableIsUnique = morkBool_kFalse; + mBuilder_TableIsVerbose = morkBool_kFalse; + + morkTable* table = mBuilder_Store->MidToTable(ev, inMid); + morkTable::SlotStrongTable(table, ev, &mBuilder_Table); + if ( table ) + { + if ( table->mTable_RowSpace ) + mBuilder_TableRowScope = table->mTable_RowSpace->SpaceScope(); + + if ( inCutAllRows ) + table->CutAllRows(ev); + } +} + +/*virtual*/ void +morkBuilder::OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "table"); +} + +/*virtual*/ void +morkBuilder::OnTableEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +{ + MORK_USED_1(inSpan); + // mParser_InTable = morkBool_kFalse; + if ( mBuilder_Table ) + { + mBuilder_Table->mTable_Priority = mBuilder_TablePriority; + + if ( mBuilder_TableIsUnique ) + mBuilder_Table->SetTableUnique(); + + if ( mBuilder_TableIsVerbose ) + mBuilder_Table->SetTableVerbose(); + + morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table); + } + else + this->NilBuilderTableError(ev); + + mBuilder_Row = 0; + mBuilder_Cell = 0; + + + mBuilder_TablePriority = morkPriority_kLo; + mBuilder_TableIsUnique = morkBool_kFalse; + mBuilder_TableIsVerbose = morkBool_kFalse; + + if ( mBuilder_TableKind == morkStore_kNoneToken ) + ev->NewError("missing table kind"); + + mBuilder_CellAtomScope = mBuilder_RowAtomScope = + mBuilder_TableAtomScope = mBuilder_PortAtomScope; + + mBuilder_DoCutCell = morkBool_kFalse; + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void +morkBuilder::OnNewMeta(morkEnv* ev, const morkPlace& inPlace) +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +// mp:MetaItem ::= mp:Cell | OnMetaGlitch +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_2(ev,inPlace); + // mParser_InMeta = morkBool_kTrue; + +} + +/*virtual*/ void +morkBuilder::OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "meta"); +} + +/*virtual*/ void +morkBuilder::OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +{ + MORK_USED_2(ev,inSpan); + // mParser_InMeta = morkBool_kFalse; +} + +/*virtual*/ void +morkBuilder::OnMinusRow(morkEnv* ev) +{ + MORK_USED_1(ev); + mBuilder_DoCutRow = morkBool_kTrue; +} + +/*virtual*/ void +morkBuilder::OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllCols) +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch +// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd +// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd +// mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inPlace); + // mParser_InRow = morkBool_kTrue; + + mBuilder_CellForm = mBuilder_RowForm = mBuilder_TableForm; + mBuilder_CellAtomScope = mBuilder_RowAtomScope = mBuilder_TableAtomScope; + mBuilder_RowRowScope = mBuilder_TableRowScope; + morkStore* store = mBuilder_Store; + + if ( !inMid.mMid_Buf && !inMid.mMid_Oid.mOid_Scope ) + { + morkMid mid(inMid); + mid.mMid_Oid.mOid_Scope = mBuilder_RowRowScope; + mBuilder_Row = store->MidToRow(ev, mid); + } + else + { + mBuilder_Row = store->MidToRow(ev, inMid); + } + morkRow* row = mBuilder_Row; + if ( row && inCutAllCols ) + { + row->CutAllColumns(ev); + } + + morkTable* table = mBuilder_Table; + if ( table ) + { + if ( row ) + { + if ( mParser_InMeta ) + { + morkRow* metaRow = table->mTable_MetaRow; + if ( !metaRow ) + { + table->mTable_MetaRow = row; + table->mTable_MetaRowOid = row->mRow_Oid; + row->AddRowGcUse(ev); + } + else if ( metaRow != row ) // not identical? + ev->NewError("duplicate table meta row"); + } + else + { + if ( mBuilder_DoCutRow ) + table->CutRow(ev, row); + else + table->AddRow(ev, row); + } + } + } + // else // it is now okay to have rows outside a table: + // this->NilBuilderTableError(ev); + + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void +morkBuilder::OnRowPos(morkEnv* ev, mork_pos inRowPos) +{ + if ( mBuilder_Row && mBuilder_Table && !mParser_InMeta ) + { + mork_pos hintFromPos = 0; // best hint when we don't know position + mBuilder_Table->MoveRow(ev, mBuilder_Row, hintFromPos, inRowPos); + } +} + +/*virtual*/ void +morkBuilder::OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "row"); +} + +void +morkBuilder::FlushBuilderCells(morkEnv* ev) +{ + if ( mBuilder_Row ) + { + morkPool* pool = mBuilder_Store->StorePool(); + morkCell* cells = mBuilder_CellsVec; + mork_fill fill = mBuilder_CellsVecFill; + mBuilder_Row->TakeCells(ev, cells, fill, mBuilder_Store); + + morkCell* end = cells + fill; + --cells; // prepare for preincrement + while ( ++cells < end ) + { + if ( cells->mCell_Atom ) + cells->SetAtom(ev, (morkAtom*) 0, pool); + } + mBuilder_CellsVecFill = 0; + } + else + this->NilBuilderRowError(ev); +} + +/*virtual*/ void +morkBuilder::OnRowEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd +{ + MORK_USED_1(inSpan); + // mParser_InRow = morkBool_kFalse; + if ( mBuilder_Row ) + { + this->FlushBuilderCells(ev); + } + else + this->NilBuilderRowError(ev); + + mBuilder_Row = 0; + mBuilder_Cell = 0; + + mBuilder_DoCutCell = morkBool_kFalse; + mBuilder_DoCutRow = morkBool_kFalse; +} + +/*virtual*/ void +morkBuilder::OnNewDict(morkEnv* ev, const morkPlace& inPlace) +// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd +// mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch +{ + MORK_USED_2(ev,inPlace); + // mParser_InDict = morkBool_kTrue; + + mBuilder_CellForm = mBuilder_DictForm = mBuilder_PortForm; + mBuilder_CellAtomScope = mBuilder_DictAtomScope = mBuilder_PortAtomScope; +} + +/*virtual*/ void +morkBuilder::OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "dict"); +} + +/*virtual*/ void +morkBuilder::OnDictEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd +{ + MORK_USED_2(ev,inSpan); + // mParser_InDict = morkBool_kFalse; + + mBuilder_DictForm = 0; + mBuilder_DictAtomScope = 0; +} + +/*virtual*/ void +morkBuilder::OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +{ + MORK_USED_1(inSpan); + if ( mParser_InDict ) + { + morkMid mid = inMid; // local copy for modification + mid.mMid_Oid.mOid_Scope = mBuilder_DictAtomScope; + mBuilder_Store->AddAlias(ev, mid, mBuilder_DictForm); + } + else + ev->NewError("alias not in dict"); +} + +/*virtual*/ void +morkBuilder::OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "alias"); +} + + +morkCell* +morkBuilder::AddBuilderCell(morkEnv* ev, + const morkMid& inMid, mork_change inChange) +{ + morkCell* outCell = 0; + mork_column column = inMid.mMid_Oid.mOid_Id; + + if ( ev->Good() ) + { + if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize ) + this->FlushBuilderCells(ev); + if ( ev->Good() ) + { + if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize ) + { + mork_fill indx = mBuilder_CellsVecFill++; + outCell = mBuilder_CellsVec + indx; + outCell->SetColumnAndChange(column, inChange); + outCell->mCell_Atom = 0; + } + else + ev->NewError("out of builder cells"); + } + } + return outCell; +} + +/*virtual*/ void +morkBuilder::OnMinusCell(morkEnv* ev) +{ + MORK_USED_1(ev); + mBuilder_DoCutCell = morkBool_kTrue; +} + +/*virtual*/ void +morkBuilder::OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, const morkBuf* inBuf) +// Exactly one of inMid and inBuf is nil, and the other is non-nil. +// When hex ID syntax is used for a column, then inMid is not nil, and +// when a naked string names a column, then inBuf is not nil. + + // mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd + // mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch + // mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inPlace); + // mParser_InCell = morkBool_kTrue; + + mork_change cellChange = ( mBuilder_DoCutCell )? + morkChange_kCut : morkChange_kAdd; + + mBuilder_DoCutCell = morkBool_kFalse; + + mBuilder_CellAtomScope = mBuilder_RowAtomScope; + + mBuilder_Cell = 0; // nil until determined for a row + morkStore* store = mBuilder_Store; + mork_scope scope = morkStore_kColumnSpaceScope; + morkMid tempMid; // space for local and modifiable cell mid + morkMid* cellMid = &tempMid; // default to local if inMid==0 + + if ( inMid ) // mid parameter is actually provided? + { + *cellMid = *inMid; // bitwise copy for modifiable local mid + + if ( !cellMid->mMid_Oid.mOid_Scope ) + { + if ( cellMid->mMid_Buf ) + { + scope = store->BufToToken(ev, cellMid->mMid_Buf); + cellMid->mMid_Buf = 0; // don't do scope lookup again + ev->NewWarning("column mids need column scope"); + } + cellMid->mMid_Oid.mOid_Scope = scope; + } + } + else if ( inBuf ) // buf points to naked column string name? + { + cellMid->ClearMid(); + cellMid->mMid_Oid.mOid_Id = store->BufToToken(ev, inBuf); + cellMid->mMid_Oid.mOid_Scope = scope; // kColumnSpaceScope + } + else + ev->NilPointerError(); // either inMid or inBuf must be non-nil + + mork_column column = cellMid->mMid_Oid.mOid_Id; + + if ( mBuilder_Row && ev->Good() ) // this cell must be inside a row + { + // mBuilder_Cell = this->AddBuilderCell(ev, *cellMid, cellChange); + + if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize ) + this->FlushBuilderCells(ev); + if ( ev->Good() ) + { + if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize ) + { + mork_fill ix = mBuilder_CellsVecFill++; + morkCell* cell = mBuilder_CellsVec + ix; + cell->SetColumnAndChange(column, cellChange); + + cell->mCell_Atom = 0; + mBuilder_Cell = cell; + } + else + ev->NewError("out of builder cells"); + } + } + + else if ( mParser_InMeta && ev->Good() ) // cell is in metainfo structure? + { + if ( scope == morkStore_kColumnSpaceScope ) + { + if ( mParser_InTable ) // metainfo for table? + { + if ( column == morkStore_kKindColumn ) + mBuilder_MetaTokenSlot = &mBuilder_TableKind; + else if ( column == morkStore_kStatusColumn ) + mBuilder_MetaTokenSlot = &mBuilder_TableStatus; + else if ( column == morkStore_kRowScopeColumn ) + mBuilder_MetaTokenSlot = &mBuilder_TableRowScope; + else if ( column == morkStore_kAtomScopeColumn ) + mBuilder_MetaTokenSlot = &mBuilder_TableAtomScope; + else if ( column == morkStore_kFormColumn ) + mBuilder_MetaTokenSlot = &mBuilder_TableForm; + } + else if ( mParser_InDict ) // metainfo for dict? + { + if ( column == morkStore_kAtomScopeColumn ) + mBuilder_MetaTokenSlot = &mBuilder_DictAtomScope; + else if ( column == morkStore_kFormColumn ) + mBuilder_MetaTokenSlot = &mBuilder_DictForm; + } + else if ( mParser_InRow ) // metainfo for row? + { + if ( column == morkStore_kAtomScopeColumn ) + mBuilder_MetaTokenSlot = &mBuilder_RowAtomScope; + else if ( column == morkStore_kRowScopeColumn ) + mBuilder_MetaTokenSlot = &mBuilder_RowRowScope; + else if ( column == morkStore_kFormColumn ) + mBuilder_MetaTokenSlot = &mBuilder_RowForm; + } + } + else + ev->NewWarning("expected column scope"); + } +} + +/*virtual*/ void +morkBuilder::OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) +{ + this->LogGlitch(ev, inGlitch, "cell"); +} + +/*virtual*/ void +morkBuilder::OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) +{ + morkCell* cell = mBuilder_Cell; + if ( cell ) + { + mBuilder_CellForm = inCharsetFormat; + } + else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void +morkBuilder::OnCellEnd(morkEnv* ev, const morkSpan& inSpan) +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +{ + MORK_USED_2(ev,inSpan); + // mParser_InCell = morkBool_kFalse; + + mBuilder_MetaTokenSlot = 0; + mBuilder_CellAtomScope = mBuilder_RowAtomScope; +} + +/*virtual*/ void +morkBuilder::OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if ( cell ) + { + mdbYarn yarn; + yarn.mYarn_Buf = inBuf.mBuf_Body; + yarn.mYarn_Fill = yarn.mYarn_Size = inBuf.mBuf_Fill; + yarn.mYarn_More = 0; + yarn.mYarn_Form = mBuilder_CellForm; + yarn.mYarn_Grow = 0; + morkAtom* atom = store->YarnToAtom(ev, &yarn, true /* create */); + cell->SetAtom(ev, atom, store->StorePool()); + } + else if ( mParser_InMeta ) + { + mork_token* metaSlot = mBuilder_MetaTokenSlot; + if ( metaSlot ) + { + if ( metaSlot == &mBuilder_TableStatus ) // table status? + { + if ( mParser_InTable && mBuilder_Table ) + { + const char* body = (const char*) inBuf.mBuf_Body; + mork_fill bufFill = inBuf.mBuf_Fill; + if ( body && bufFill ) + { + const char* bodyEnd = body + bufFill; + while ( body < bodyEnd ) + { + int c = *body++; + switch ( c ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + mBuilder_TablePriority = (mork_priority) ( c - '0' ); + break; + + case 'u': + case 'U': + mBuilder_TableIsUnique = morkBool_kTrue; + break; + + case 'v': + case 'V': + mBuilder_TableIsVerbose = morkBool_kTrue; + break; + } + } + } + } + } + else + { + mork_token token = store->BufToToken(ev, &inBuf); + if ( token ) + { + *metaSlot = token; + if ( metaSlot == &mBuilder_TableKind ) // table kind? + { + if ( mParser_InTable && mBuilder_Table ) + mBuilder_Table->mTable_Kind = token; + } + } + } + } + } + else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void +morkBuilder::OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + + morkMid valMid; // local mid for modifications + mdbOid* valOid = &valMid.mMid_Oid; // ref to oid inside mid + *valOid = inMid.mMid_Oid; // bitwise copy inMid's oid + + if ( inMid.mMid_Buf ) + { + if ( !valOid->mOid_Scope ) + store->MidToOid(ev, inMid, valOid); + } + else if ( !valOid->mOid_Scope ) + valOid->mOid_Scope = mBuilder_CellAtomScope; + + if ( cell ) + { + morkBookAtom* atom = store->MidToAtom(ev, valMid); + if ( atom ) + cell->SetAtom(ev, atom, store->StorePool()); + else + ev->NewError("undefined cell value alias"); + } + else if ( mParser_InMeta ) + { + mork_token* metaSlot = mBuilder_MetaTokenSlot; + if ( metaSlot ) + { + mork_scope valScope = valOid->mOid_Scope; + if ( !valScope || valScope == morkStore_kColumnSpaceScope ) + { + if ( ev->Good() && valMid.HasSomeId() ) + { + *metaSlot = valOid->mOid_Id; + if ( metaSlot == &mBuilder_TableKind ) // table kind? + { + if ( mParser_InTable && mBuilder_Table ) + { + mBuilder_Table->mTable_Kind = valOid->mOid_Id; + } + else + ev->NewWarning("mBuilder_TableKind not in table"); + } + else if ( metaSlot == &mBuilder_TableStatus ) // table status? + { + if ( mParser_InTable && mBuilder_Table ) + { + // $$ what here?? + } + else + ev->NewWarning("mBuilder_TableStatus not in table"); + } + } + } + else + this->NonColumnSpaceScopeError(ev); + } + } + else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void +morkBuilder::OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if ( cell ) + { + mdbOid rowOid = inMid.mMid_Oid; + if ( inMid.mMid_Buf ) + { + if ( !rowOid.mOid_Scope ) + store->MidToOid(ev, inMid, &rowOid); + } + else if ( !rowOid.mOid_Scope ) + rowOid.mOid_Scope = mBuilder_RowRowScope; + + if ( ev->Good() ) + { + morkPool* pool = store->StorePool(); + morkAtom* atom = pool->NewRowOidAtom(ev, rowOid, &store->mStore_Zone); + if ( atom ) + { + cell->SetAtom(ev, atom, pool); + morkRow* row = store->OidToRow(ev, &rowOid); + if ( row ) // found or created such a row? + row->AddRowGcUse(ev); + } + } + } + else + this->NilBuilderCellError(ev); +} + +/*virtual*/ void +morkBuilder::OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid +{ + MORK_USED_1(inSpan); + morkStore* store = mBuilder_Store; + morkCell* cell = mBuilder_Cell; + if ( cell ) + { + mdbOid tableOid = inMid.mMid_Oid; + if ( inMid.mMid_Buf ) + { + if ( !tableOid.mOid_Scope ) + store->MidToOid(ev, inMid, &tableOid); + } + else if ( !tableOid.mOid_Scope ) + tableOid.mOid_Scope = mBuilder_RowRowScope; + + if ( ev->Good() ) + { + morkPool* pool = store->StorePool(); + morkAtom* atom = pool->NewTableOidAtom(ev, tableOid, &store->mStore_Zone); + if ( atom ) + { + cell->SetAtom(ev, atom, pool); + morkTable* table = store->OidToTable(ev, &tableOid, + /*optionalMetaRowOid*/ (mdbOid*) 0); + if ( table ) // found or created such a table? + table->AddTableGcUse(ev); + } + } + } + else + this->NilBuilderCellError(ev); +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkBuilder.h b/db/mork/src/morkBuilder.h new file mode 100644 index 0000000000..cd8b9527ab --- /dev/null +++ b/db/mork/src/morkBuilder.h @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKBUILDER_ +#define _MORKBUILDER_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKPARSER_ +#include "morkParser.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kCellsVecSize: length of cell vector buffer inside morkBuilder +|*/ +#define morkBuilder_kCellsVecSize 64 + +#define morkBuilder_kDefaultBytesPerParseSegment 512 /* plausible to big */ + +#define morkDerived_kBuilder /*i*/ 0x4275 /* ascii 'Bu' */ + +class morkBuilder /*d*/ : public morkParser { + +// public: // slots inherited from morkParser (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + + // nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation + // morkStream* mParser_Stream; // refcounted input stream + + // mork_u4 mParser_Tag; // must equal morkParser_kTag + // mork_count mParser_MoreGranularity; // constructor inBytesPerParseSegment + + // mork_u4 mParser_State; // state where parser should resume + + // after finding ends of group transactions, we can re-seek the start: + // mork_pos mParser_GroupContentStartPos; // start of this group + + // mdbOid mParser_TableOid; // table oid if inside a table + // mdbOid mParser_RowOid; // row oid if inside a row + // mork_gid mParser_GroupId; // group ID if inside a group + + // mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd? + // mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd? + // mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd? + // mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd? + + // morkMid mParser_Mid; // current alias being parsed + // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below: + + // blob coils allocated in mParser_Heap + // morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs + // morkCoil mParser_ValueCoil; // place to accumulate value blobs + // morkCoil mParser_ColumnCoil; // place to accumulate column blobs + // morkCoil mParser_StringCoil; // place to accumulate string blobs + + // morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil + // morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil + // morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil + // morkSpool mParser_StringSpool; // writes to mParser_StringCoil + + // yarns allocated in mParser_Heap + // morkYarn mParser_MidYarn; // place to receive from MidToYarn() + + // span showing current ongoing file position status: + // morkSpan mParser_PortSpan; // span of current db port file + + // various spans denoting nested subspaces inside the file's port span: + // morkSpan mParser_GroupSpan; // span of current transaction group + // morkSpan mParser_DictSpan; + // morkSpan mParser_AliasSpan; + // morkSpan mParser_MetaDictSpan; + // morkSpan mParser_TableSpan; + // morkSpan mParser_MetaTableSpan; + // morkSpan mParser_RowSpan; + // morkSpan mParser_MetaRowSpan; + // morkSpan mParser_CellSpan; + // morkSpan mParser_ColumnSpan; + // morkSpan mParser_SlotSpan; + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected morkBuilder members + + // weak refs that do not prevent closure of referenced nodes: + morkStore* mBuilder_Store; // weak ref to builder's store + + // strong refs that do indeed prevent closure of referenced nodes: + morkTable* mBuilder_Table; // current table being built (or nil) + morkRow* mBuilder_Row; // current row being built (or nil) + morkCell* mBuilder_Cell; // current cell within CellsVec (or nil) + + morkRowSpace* mBuilder_RowSpace; // space for mBuilder_CellRowScope + morkAtomSpace* mBuilder_AtomSpace; // space for mBuilder_CellAtomScope + + morkAtomSpace* mBuilder_OidAtomSpace; // ground atom space for oids + morkAtomSpace* mBuilder_ScopeAtomSpace; // ground atom space for scopes + + // scoped object ids for current objects under construction: + mdbOid mBuilder_TableOid; // full oid for current table + mdbOid mBuilder_RowOid; // full oid for current row + + // tokens that become set as the result of meta cells in port rows: + mork_cscode mBuilder_PortForm; // default port charset format + mork_scope mBuilder_PortRowScope; // port row scope + mork_scope mBuilder_PortAtomScope; // port atom scope + + // tokens that become set as the result of meta cells in meta tables: + mork_cscode mBuilder_TableForm; // default table charset format + mork_scope mBuilder_TableRowScope; // table row scope + mork_scope mBuilder_TableAtomScope; // table atom scope + mork_kind mBuilder_TableKind; // table kind + + mork_token mBuilder_TableStatus; // dummy: priority/unique/verbose + + mork_priority mBuilder_TablePriority; // table priority + mork_bool mBuilder_TableIsUnique; // table uniqueness + mork_bool mBuilder_TableIsVerbose; // table verboseness + mork_u1 mBuilder_TablePadByte; // for u4 alignment + + // tokens that become set as the result of meta cells in meta rows: + mork_cscode mBuilder_RowForm; // default row charset format + mork_scope mBuilder_RowRowScope; // row scope per row metainfo + mork_scope mBuilder_RowAtomScope; // row atom scope + + // meta tokens currently in force, driven by meta info slots above: + mork_cscode mBuilder_CellForm; // cell charset format + mork_scope mBuilder_CellAtomScope; // cell atom scope + + mork_cscode mBuilder_DictForm; // dict charset format + mork_scope mBuilder_DictAtomScope; // dict atom scope + + mork_token* mBuilder_MetaTokenSlot; // pointer to some slot above + + // If any of these 'cut' bools are true, it means a minus was seen in the + // Mork source text to indicate removal of content from some container. + // (Note there is no corresponding 'add' bool, since add is the default.) + // CutRow implies the current row should be cut from the table. + // CutCell implies the current column should be cut from the row. + mork_bool mBuilder_DoCutRow; // row with kCut change + mork_bool mBuilder_DoCutCell; // cell with kCut change + mork_u1 mBuilder_row_pad; // pad to u4 alignment + mork_u1 mBuilder_cell_pad; // pad to u4 alignment + + morkCell mBuilder_CellsVec[ morkBuilder_kCellsVecSize + 1 ]; + mork_fill mBuilder_CellsVecFill; // count used in CellsVec + // Note when mBuilder_CellsVecFill equals morkBuilder_kCellsVecSize, and + // another cell is added, this means all the cells in the vector above + // must be flushed to the current row being built to create more room. + +protected: // protected inlines + + mork_bool CellVectorIsFull() const + { return ( mBuilder_CellsVecFill == morkBuilder_kCellsVecSize ); } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseBuilder() only if open + virtual ~morkBuilder(); // assert that CloseBuilder() executed earlier + +public: // morkYarn construction & destruction + morkBuilder(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, // the readonly stream for input bytes + mdb_count inBytesPerParseSegment, // target for ParseMore() + nsIMdbHeap* ioSlotHeap, morkStore* ioStore + ); + + void CloseBuilder(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkBuilder(const morkBuilder& other); + morkBuilder& operator=(const morkBuilder& other); + +public: // dynamic type identification + mork_bool IsBuilder() const + { return IsNode() && mNode_Derived == morkDerived_kBuilder; } +// } ===== end morkNode methods ===== + +public: // errors + static void NonBuilderTypeError(morkEnv* ev); + static void NilBuilderCellError(morkEnv* ev); + static void NilBuilderRowError(morkEnv* ev); + static void NilBuilderTableError(morkEnv* ev); + static void NonColumnSpaceScopeError(morkEnv* ev); + + void LogGlitch(morkEnv* ev, const morkGlitch& inGlitch, + const char* inKind); + +public: // other builder methods + + morkCell* AddBuilderCell(morkEnv* ev, + const morkMid& inMid, mork_change inChange); + + void FlushBuilderCells(morkEnv* ev); + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // in virtual morkParser methods, data flow subclass to parser + + virtual void MidToYarn(morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) override; + // The parser might ask that some aliases be turned into yarns, so they + // can be concatenated into longer blobs under some circumstances. This + // is an alternative to using a long and complex callback for many parts + // for a single cell value. + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // out virtual morkParser methods, data flow parser to subclass + + virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) override; + virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) override; + virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_change inChange) override; + virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllRows) override; + virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnMinusRow(morkEnv* ev) override; + virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllCols) override; + virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) override; + virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) override; + virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + + virtual void OnMinusCell(morkEnv* ev) override; + virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, const morkBuf* inBuf) override; + // Exactly one of inMid and inBuf is nil, and the other is non-nil. + // When hex ID syntax is used for a column, then inMid is not nil, and + // when a naked string names a column, then inBuf is not nil. + + virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) override; + virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) override; + virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) override; + + virtual void OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) override; + + virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + + virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) override; + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkBuilder methods + + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakBuilder(morkBuilder* me, + morkEnv* ev, morkBuilder** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongBuilder(morkBuilder* me, + morkEnv* ev, morkBuilder** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKBUILDER_ */ diff --git a/db/mork/src/morkCell.cpp b/db/mork/src/morkCell.cpp new file mode 100644 index 0000000000..c5844dd1a6 --- /dev/null +++ b/db/mork/src/morkCell.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +void +morkCell::SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore) +{ + morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */); + if ( atom ) + this->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom +} + +void +morkCell::GetYarn(morkEnv* ev, mdbYarn* outYarn) const +{ + MORK_USED_1(ev); + mCell_Atom->GetYarn(outYarn); +} + +void +morkCell::AliasYarn(morkEnv* ev, mdbYarn* outYarn) const +{ + MORK_USED_1(ev); + morkAtom::AliasYarn(mCell_Atom, outYarn); +} + +void +morkCell::SetCellClean() +{ + mork_column col = this->GetColumn(); + this->SetColumnAndChange(col, morkChange_kNil); +} + +void +morkCell::SetCellDirty() +{ + mork_column col = this->GetColumn(); + this->SetColumnAndChange(col, morkChange_kAdd); +} + +void +morkCell::SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool) + // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse() + // to increase the refcount, and puts ioAtom into mCell_Atom. If the old + // atom in mCell_Atom is non-nil, then it is "released" first by a call to + // CutCellUse(), and if the use count then becomes zero, then the old atom + // is deallocated by returning it to the pool ioPool. (And this is + // why ioPool is a parameter to this method.) Note that ioAtom can be nil + // to cause the cell to refer to nothing, and the old atom in mCell_Atom + // can also be nil, and all the atom refcounting is handled correctly. + // + // Note that if ioAtom was just created, it typically has a zero use count + // before calling SetAtom(). But use count is one higher after SetAtom(). +{ + morkAtom* oldAtom = mCell_Atom; + if ( oldAtom != ioAtom ) // ioAtom is not already installed in this cell? + { + if ( oldAtom ) + { + mCell_Atom = 0; + if ( oldAtom->CutCellUse(ev) == 0 ) + { + // this was zapping atoms still in use - comment out until davidmc + // can figure out a better fix. +// if ( ioPool ) +// { +// if ( oldAtom->IsBook() ) +// ((morkBookAtom*) oldAtom)->CutBookAtomFromSpace(ev); + +// ioPool->ZapAtom(ev, oldAtom); +// } +// else +// ev->NilPointerError(); + } + } + if ( ioAtom ) + ioAtom->AddCellUse(ev); + + mCell_Atom = ioAtom; + } +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkCell.h b/db/mork/src/morkCell.h new file mode 100644 index 0000000000..50642d9169 --- /dev/null +++ b/db/mork/src/morkCell.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKCELL_ +#define _MORKCELL_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDelta_kShift 8 /* 8 bit shift */ +#define morkDelta_kChangeMask 0x0FF /* low 8 bit mask */ +#define morkDelta_kColumnMask (~ (mork_column) morkDelta_kChangeMask) +#define morkDelta_Init(self,cl,ch) ((self) = ((cl)<> morkDelta_kShift) + +class morkCell { // minimal cell format + +public: + mork_delta mCell_Delta; // encoding of both column and change + morkAtom* mCell_Atom; // content in this cell + +public: + morkCell() : mCell_Delta( 0 ), mCell_Atom( 0 ) { } + + morkCell(const morkCell& c) + : mCell_Delta( c.mCell_Delta ), mCell_Atom( c.mCell_Atom ) { } + + // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse(): + morkCell(mork_column inCol, mork_change inChange, morkAtom* ioAtom) + { + morkDelta_Init(mCell_Delta, inCol,inChange); + mCell_Atom = ioAtom; + } + + // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse(): + void Init(mork_column inCol, mork_change inChange, morkAtom* ioAtom) + { + morkDelta_Init(mCell_Delta,inCol,inChange); + mCell_Atom = ioAtom; + } + + mork_column GetColumn() const { return morkDelta_Column(mCell_Delta); } + mork_change GetChange() const { return morkDelta_Change(mCell_Delta); } + + mork_bool IsCellClean() const { return GetChange() == morkChange_kNil; } + mork_bool IsCellDirty() const { return GetChange() != morkChange_kNil; } + + void SetCellClean(); // set change to kNil + void SetCellDirty(); // set change to kAdd + + void SetCellColumnDirty(mork_column inCol) + { this->SetColumnAndChange(inCol, morkChange_kAdd); } + + void SetCellColumnClean(mork_column inCol) + { this->SetColumnAndChange(inCol, morkChange_kNil); } + + void SetColumnAndChange(mork_column inCol, mork_change inChange) + { morkDelta_Init(mCell_Delta, inCol, inChange); } + + morkAtom* GetAtom() { return mCell_Atom; } + + void SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool); + // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse() + // to increase the refcount, and puts ioAtom into mCell_Atom. If the old + // atom in mCell_Atom is non-nil, then it is "released" first by a call to + // CutCellUse(), and if the use count then becomes zero, then the old atom + // is deallocated by returning it to the pool ioPool. (And this is + // why ioPool is a parameter to this method.) Note that ioAtom can be nil + // to cause the cell to refer to nothing, and the old atom in mCell_Atom + // can also be nil, and all the atom refcounting is handled correctly. + // + // Note that if ioAtom was just created, it typically has a zero use count + // before calling SetAtom(). But use count is one higher after SetAtom(). + + void SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore); + + void AliasYarn(morkEnv* ev, mdbYarn* outYarn) const; + void GetYarn(morkEnv* ev, mdbYarn* outYarn) const; +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCELL_ */ diff --git a/db/mork/src/morkCellObject.cpp b/db/mork/src/morkCellObject.cpp new file mode 100644 index 0000000000..5310df07d1 --- /dev/null +++ b/db/mork/src/morkCellObject.cpp @@ -0,0 +1,530 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCELLOBJECT_ +#include "morkCellObject.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkCellObject::CloseMorkNode(morkEnv* ev) // CloseCellObject() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseCellObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkCellObject::~morkCellObject() // assert CloseCellObject() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(mCellObject_Row==0); +} + +/*public non-poly*/ +morkCellObject::morkCellObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell, + mork_column inCol, mork_pos inPos) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mCellObject_RowObject( 0 ) +, mCellObject_Row( 0 ) +, mCellObject_Cell( 0 ) +, mCellObject_Col( inCol ) +, mCellObject_RowSeed( 0 ) +, mCellObject_Pos( (mork_u2) inPos ) +{ + if ( ev->Good() ) + { + if ( ioRow && ioCell ) + { + if ( ioRow->IsRow() ) + { + morkStore* store = ioRow->GetRowSpaceStore(ev); + if ( store ) + { + morkRowObject* rowObj = ioRow->AcquireRowObject(ev, store); + if ( rowObj ) + { + mCellObject_Row = ioRow; + mCellObject_Cell = ioCell; + mCellObject_RowSeed = ioRow->mRow_Seed; + + // morkRowObject::SlotStrongRowObject(rowObj, ev, + // &mCellObject_RowObject); + + mCellObject_RowObject = rowObj; // assume control of strong ref + } + if ( ev->Good() ) + mNode_Derived = morkDerived_kCellObject; + } + } + else + ioRow->NonRowTypeError(ev); + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkCellObject, morkObject, nsIMdbCell) + +/*public non-poly*/ void +morkCellObject::CloseCellObject(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + NS_RELEASE(mCellObject_RowObject); + mCellObject_Row = 0; + mCellObject_Cell = 0; + mCellObject_RowSeed = 0; + this->CloseObject(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool +morkCellObject::ResyncWithRow(morkEnv* ev) +{ + morkRow* row = mCellObject_Row; + mork_pos pos = 0; + morkCell* cell = row->GetCell(ev, mCellObject_Col, &pos); + if ( cell ) + { + mCellObject_Pos = (mork_u2) pos; + mCellObject_Cell = cell; + mCellObject_RowSeed = row->mRow_Seed; + } + else + { + mCellObject_Cell = 0; + this->MissingRowColumnError(ev); + } + return ev->Good(); +} + +morkAtom* +morkCellObject::GetCellAtom(morkEnv* ev) const +{ + morkCell* cell = mCellObject_Cell; + if ( cell ) + return cell->GetAtom(); + else + this->NilCellError(ev); + + return (morkAtom*) 0; +} + +/*static*/ void +morkCellObject::WrongRowObjectRowError(morkEnv* ev) +{ + ev->NewError("mCellObject_Row != mCellObject_RowObject->mRowObject_Row"); +} + +/*static*/ void +morkCellObject::NilRowError(morkEnv* ev) +{ + ev->NewError("nil mCellObject_Row"); +} + +/*static*/ void +morkCellObject::NilRowObjectError(morkEnv* ev) +{ + ev->NewError("nil mCellObject_RowObject"); +} + +/*static*/ void +morkCellObject::NilCellError(morkEnv* ev) +{ + ev->NewError("nil mCellObject_Cell"); +} + +/*static*/ void +morkCellObject::NonCellObjectTypeError(morkEnv* ev) +{ + ev->NewError("non morkCellObject"); +} + +/*static*/ void +morkCellObject::MissingRowColumnError(morkEnv* ev) +{ + ev->NewError("mCellObject_Col not in mCellObject_Row"); +} + +nsIMdbCell* +morkCellObject::AcquireCellHandle(morkEnv* ev) +{ + nsIMdbCell* outCell = this; + NS_ADDREF(outCell); + return outCell; +} + + +morkEnv* +morkCellObject::CanUseCell(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr, morkCell** outCell) +{ + morkEnv* outEnv = 0; + morkCell* cell = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( IsCellObject() ) + { + if ( IsMutable() || !inMutable ) + { + morkRowObject* rowObj = mCellObject_RowObject; + if ( rowObj ) + { + morkRow* row = mCellObject_Row; + if ( row ) + { + if ( rowObj->mRowObject_Row == row ) + { + mork_u2 oldSeed = mCellObject_RowSeed; + if ( row->mRow_Seed == oldSeed || ResyncWithRow(ev) ) + { + cell = mCellObject_Cell; + if ( cell ) + { + outEnv = ev; + } + else + NilCellError(ev); + } + } + else + WrongRowObjectRowError(ev); + } + else + NilRowError(ev); + } + else + NilRowObjectError(ev); + } + else + NonMutableNodeError(ev); + } + else + NonCellObjectTypeError(ev); + } + *outErr = ev->AsErr(); + MORK_ASSERT(outEnv); + *outCell = cell; + + return outEnv; +} + +// { ----- begin attribute methods ----- +NS_IMETHODIMP morkCellObject::SetBlob(nsIMdbEnv* /* mev */, + nsIMdbBlob* /* ioBlob */) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // reads inBlob slots + +// when inBlob is in the same suite, this might be fastest cell-to-cell + +NS_IMETHODIMP morkCellObject::ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* /* mev */) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} +// clearing a yarn is like SetYarn() with empty yarn instance content + +NS_IMETHODIMP morkCellObject::GetBlobFill(nsIMdbEnv* mev, + mdb_fill* outFill) +// Same value that would be put into mYarn_Fill, if one called GetYarn() +// with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // size of blob + +NS_IMETHODIMP morkCellObject::SetYarn(nsIMdbEnv* mev, + const mdbYarn* inYarn) +{ + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + morkRow* row = mCellObject_Row; + if ( row ) + { + morkStore* store = row->GetRowSpaceStore(ev); + if ( store ) + { + cell->SetYarn(ev, inYarn, store); + if ( row->IsRowClean() && store->mStore_CanDirty ) + row->MaybeDirtySpaceStoreAndRow(); + } + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + + return outErr; +} // reads from yarn slots +// make this text object contain content from the yarn's buffer + +NS_IMETHODIMP morkCellObject::GetYarn(nsIMdbEnv* mev, + mdbYarn* outYarn) +{ + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + morkAtom* atom = cell->GetAtom(); + atom->GetYarn(outYarn); + outErr = ev->AsErr(); + } + + return outErr; +} // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + +NS_IMETHODIMP morkCellObject::AliasYarn(nsIMdbEnv* mev, + mdbYarn* outYarn) +{ + nsresult outErr = NS_OK; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + morkAtom* atom = cell->GetAtom(); + morkAtom::AliasYarn(atom, outYarn); + outErr = ev->AsErr(); + } + + return outErr; +} // writes ALL yarn slots + +// } ----- end attribute methods ----- + +// } ===== end nsIMdbBlob methods ===== + +// { ===== begin nsIMdbCell methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP morkCellObject::SetColumn(nsIMdbEnv* mev, mdb_column inColumn) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} + +NS_IMETHODIMP morkCellObject::GetColumn(nsIMdbEnv* mev, mdb_column* outColumn) +{ + nsresult outErr = NS_OK; + mdb_column col = 0; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + col = mCellObject_Col; + outErr = ev->AsErr(); + } + if ( outColumn ) + *outColumn = col; + return outErr; +} + +NS_IMETHODIMP morkCellObject::GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* mev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) // nonzero if child, and a row child +// Checking all cell metainfo is a good way to avoid forcing a large cell +// in to memory when you don't actually want to use the content. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP morkCellObject::GetRow(nsIMdbEnv* mev, // parent row for this cell + nsIMdbRow** acqRow) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + outRow = mCellObject_RowObject->AcquireRowHandle(ev); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP morkCellObject::GetPort(nsIMdbEnv* mev, // port containing cell + nsIMdbPort** acqPort) +{ + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + if ( mCellObject_Row ) + { + morkStore* store = mCellObject_Row->GetRowSpaceStore(ev); + if ( store ) + outPort = store->AcquireStoreHandle(ev); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqPort ) + *acqPort = outPort; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin children methods ----- +NS_IMETHODIMP morkCellObject::HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* mev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) // nonzero if child is a row (rather than a table) +{ + nsresult outErr = NS_OK; + mdb_bool isRow = morkBool_kFalse; + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + morkCell* cell = 0; + morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue, + &outErr, &cell); + if ( ev ) + { + morkAtom* atom = GetCellAtom(ev); + if ( atom ) + { + isRow = atom->IsRowOid(); + if ( isRow || atom->IsTableOid() ) + *outOid = ((morkOidAtom*) atom)->mOidAtom_Oid; + } + + outErr = ev->AsErr(); + } + if ( outIsRow ) + *outIsRow = isRow; + + return outErr; +} + +NS_IMETHODIMP morkCellObject::GetAnyChild( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) // child table (or null) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP morkCellObject::SetChildRow( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} // inRow must be bound inside this same db port + +NS_IMETHODIMP morkCellObject::GetChildRow( // access row of specific attribute + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow) // acquire child row (or nil if no child) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP morkCellObject::SetChildTable( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbTable* inTable) // table must be bound inside this same db port +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} + +NS_IMETHODIMP morkCellObject::GetChildTable( // access table of specific attribute + nsIMdbEnv* mev, // context + nsIMdbTable** acqTable) // acquire child tabdle (or nil if no chil) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end children methods ----- + +// } ===== end nsIMdbCell methods ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkCellObject.h b/db/mork/src/morkCellObject.h new file mode 100644 index 0000000000..fced0accde --- /dev/null +++ b/db/mork/src/morkCellObject.h @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKCELLOBJECT_ +#define _MORKCELLOBJECT_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kCellObject /*i*/ 0x634F /* ascii 'cO' */ + +class morkCellObject : public morkObject, public nsIMdbCell { // blob attribute in column scope + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + morkRowObject* mCellObject_RowObject; // strong ref to row's object + morkRow* mCellObject_Row; // cell's row if still in row object + morkCell* mCellObject_Cell; // cell in row if rowseed matches + mork_column mCellObject_Col; // col of cell last living in pos + mork_u2 mCellObject_RowSeed; // copy of row's seed + mork_u2 mCellObject_Pos; // position of cell in row + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseCellObject() only if open + +public: // morkCellObject construction & destruction + morkCellObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell, + mork_column inCol, mork_pos inPos); + void CloseCellObject(morkEnv* ev); // called by CloseMorkNode(); + + NS_IMETHOD SetBlob(nsIMdbEnv* ev, + nsIMdbBlob* ioBlob) override; // reads inBlob slots + // when inBlob is in the same suite, this might be fastest cell-to-cell + + NS_IMETHOD ClearBlob( // make empty (so content has zero length) + nsIMdbEnv* ev) override; + // clearing a yarn is like SetYarn() with empty yarn instance content + + NS_IMETHOD GetBlobFill(nsIMdbEnv* ev, + mdb_fill* outFill) override; // size of blob + // Same value that would be put into mYarn_Fill, if one called GetYarn() + // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0. + + NS_IMETHOD SetYarn(nsIMdbEnv* ev, + const mdbYarn* inYarn) override; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasYarn(nsIMdbEnv* ev, + mdbYarn* outYarn) override; // writes ALL yarn slots + NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) override; + NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) override; + + NS_IMETHOD GetCellInfo( // all cell metainfo except actual content + nsIMdbEnv* ev, + mdb_column* outColumn, // the column in the containing row + mdb_fill* outBlobFill, // the size of text content in bytes + mdbOid* outChildOid, // oid of possible row or table child + mdb_bool* outIsRowChild) override; // nonzero if child, and a row child + + // Checking all cell metainfo is a good way to avoid forcing a large cell + // in to memory when you don't actually want to use the content. + + NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell + nsIMdbRow** acqRow) override; + NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell + nsIMdbPort** acqPort) override; + // } ----- end attribute methods ----- + + // { ----- begin children methods ----- + NS_IMETHOD HasAnyChild( // does cell have a child instead of text? + nsIMdbEnv* ev, + mdbOid* outOid, // out id of row or table (or unbound if no child) + mdb_bool* outIsRow) override; // nonzero if child is a row (rather than a table) + + NS_IMETHOD GetAnyChild( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // child row (or null) + nsIMdbTable** acqTable) override; // child table (or null) + + + NS_IMETHOD SetChildRow( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) override; // inRow must be bound inside this same db port + + NS_IMETHOD GetChildRow( // access row of specific attribute + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow) override; // acquire child row (or nil if no child) + + + NS_IMETHOD SetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable* inTable) override; // table must be bound inside this same db port + + NS_IMETHOD GetChildTable( // access table of specific attribute + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) override; // acquire child table (or nil if no child) + // } ----- end children methods ----- + +// } ===== end nsIMdbCell methods ===== +private: // copying is not allowed + virtual ~morkCellObject(); // assert that CloseCellObject() executed earlier + morkCellObject(const morkCellObject& other); + morkCellObject& operator=(const morkCellObject& other); + +public: // dynamic type identification + mork_bool IsCellObject() const + { return IsNode() && mNode_Derived == morkDerived_kCellObject; } +// } ===== end morkNode methods ===== + +public: // other cell node methods + + morkEnv* CanUseCell(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr, morkCell** outCell); + + mork_bool ResyncWithRow(morkEnv* ev); // return ev->Good() + morkAtom* GetCellAtom(morkEnv* ev) const; + + static void MissingRowColumnError(morkEnv* ev); + static void NilRowError(morkEnv* ev); + static void NilCellError(morkEnv* ev); + static void NilRowObjectError(morkEnv* ev); + static void WrongRowObjectRowError(morkEnv* ev); + static void NonCellObjectTypeError(morkEnv* ev); + + nsIMdbCell* AcquireCellHandle(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakCellObject(morkCellObject* me, + morkEnv* ev, morkCellObject** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongCellObject(morkCellObject* me, + morkEnv* ev, morkCellObject** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCELLOBJECT_ */ diff --git a/db/mork/src/morkCh.cpp b/db/mork/src/morkCh.cpp new file mode 100644 index 0000000000..07162a3151 --- /dev/null +++ b/db/mork/src/morkCh.cpp @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCH_ +#include "morkCh.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* this byte char predicate source file derives from public domain Mithril */ +/* (that means much of this has a copyright dedicated to the public domain) */ + +/*============================================================================*/ +/* morkCh_Type */ + +const mork_flags morkCh_Type[] = /* derives from public domain Mithril table */ +{ + 0, /* 0x0 */ + 0, /* 0x1 */ + 0, /* 0x2 */ + 0, /* 0x3 */ + 0, /* 0x4 */ + 0, /* 0x5 */ + 0, /* 0x6 */ + 0, /* 0x7 */ + morkCh_kW, /* 0x8 backspace */ + morkCh_kW, /* 0x9 tab */ + morkCh_kW, /* 0xA linefeed */ + 0, /* 0xB */ + morkCh_kW, /* 0xC page */ + morkCh_kW, /* 0xD return */ + 0, /* 0xE */ + 0, /* 0xF */ + 0, /* 0x10 */ + 0, /* 0x11 */ + 0, /* 0x12 */ + 0, /* 0x13 */ + 0, /* 0x14 */ + 0, /* 0x15 */ + 0, /* 0x16 */ + 0, /* 0x17 */ + 0, /* 0x18 */ + 0, /* 0x19 */ + 0, /* 0x1A */ + 0, /* 0x1B */ + 0, /* 0x1C */ + 0, /* 0x1D */ + 0, /* 0x1E */ + 0, /* 0x1F */ + + morkCh_kV|morkCh_kW, /* 0x20 space */ + morkCh_kV|morkCh_kM, /* 0x21 ! */ + morkCh_kV, /* 0x22 " */ + morkCh_kV, /* 0x23 # */ + 0, /* 0x24 $ cannot be kV because needs escape */ + morkCh_kV, /* 0x25 % */ + morkCh_kV, /* 0x26 & */ + morkCh_kV, /* 0x27 ' */ + morkCh_kV, /* 0x28 ( */ + 0, /* 0x29 ) cannot be kV because needs escape */ + morkCh_kV, /* 0x2A * */ + morkCh_kV|morkCh_kM, /* 0x2B + */ + morkCh_kV, /* 0x2C , */ + morkCh_kV|morkCh_kM, /* 0x2D - */ + morkCh_kV, /* 0x2E . */ + morkCh_kV, /* 0x2F / */ + + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x30 0 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x31 1 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x32 2 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x33 3 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x34 4 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x35 5 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x36 6 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x37 7 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x38 8 */ + morkCh_kV|morkCh_kD|morkCh_kX, /* 0x39 9 */ + morkCh_kV|morkCh_kN|morkCh_kM, /* 0x3A : */ + morkCh_kV, /* 0x3B ; */ + morkCh_kV, /* 0x3C < */ + morkCh_kV, /* 0x3D = */ + morkCh_kV, /* 0x3E > */ + morkCh_kV|morkCh_kM, /* 0x3F ? */ + + morkCh_kV, /* 0x40 @ */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x41 A */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x42 B */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x43 C */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x44 D */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x45 E */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x46 F */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x47 G */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x48 H */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x49 I */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4A J */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4B K */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4C L */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4D M */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4E N */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4F O */ + + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x50 P */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x51 Q */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x52 R */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x53 S */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x54 T */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x55 U */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x56 V */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x57 W */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x58 X */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x59 Y */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x5A Z */ + morkCh_kV, /* 0x5B [ */ + 0, /* 0x5C \ cannot be kV because needs escape */ + morkCh_kV, /* 0x5D ] */ + morkCh_kV, /* 0x5E ^ */ + morkCh_kV|morkCh_kN|morkCh_kM, /* 0x5F _ */ + + morkCh_kV, /* 0x60 ` */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x61 a */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x62 b */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x63 c */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x64 d */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x65 e */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x66 f */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x67 g */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x68 h */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x69 i */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6A j */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6B k */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6C l */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6D m */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6E n */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6F o */ + + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x70 p */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x71 q */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x72 r */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x73 s */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x74 t */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x75 u */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x76 v */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x77 w */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x78 x */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x79 y */ + morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x7A z */ + morkCh_kV, /* 0x7B { */ + morkCh_kV, /* 0x7C | */ + morkCh_kV, /* 0x7D } */ + morkCh_kV, /* 0x7E ~ */ + morkCh_kW, /* 0x7F rubout */ + +/* $"80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* $"F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF" */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkCh.h b/db/mork/src/morkCh.h new file mode 100644 index 0000000000..1d77ec64e1 --- /dev/null +++ b/db/mork/src/morkCh.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKCH_ +#define _MORKCH_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +/* this byte char predicate header file derives from public domain Mithril */ +/* (that means much of this has a copyright dedicated to the public domain) */ + +/* Use all 8 pred bits; lose some pred bits only if we need to reuse them. */ + +/* ch pred bits: W:white D:digit V:value U:upper L:lower N:name M:more */ +#define morkCh_kW (1 << 0) +#define morkCh_kD (1 << 1) +#define morkCh_kV (1 << 2) +#define morkCh_kU (1 << 3) +#define morkCh_kL (1 << 4) +#define morkCh_kX (1 << 5) +#define morkCh_kN (1 << 6) +#define morkCh_kM (1 << 7) + +extern const mork_flags morkCh_Type[]; /* 256 byte predicate bits ch map */ + +/* is a numeric decimal digit: (note memory access might be slower) */ +/* define morkCh_IsDigit(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kD ) */ +#define morkCh_IsDigit(c) ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '9' ) + +/* is a numeric octal digit: */ +#define morkCh_IsOctal(c) ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '7' ) + +/* is a numeric hexadecimal digit: */ +#define morkCh_IsHex(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kX ) + +/* is value (can be printed in Mork value without needing hex or escape): */ +#define morkCh_IsValue(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kV ) + +/* is white space : */ +#define morkCh_IsWhite(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kW ) + +/* is name (can start a Mork name): */ +#define morkCh_IsName(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kN ) + +/* is name (can continue a Mork name): */ +#define morkCh_IsMore(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kM ) + +/* is alphabetic upper or lower case */ +#define morkCh_IsAlpha(c) \ + ( morkCh_Type[ (mork_ch)(c) ] & (morkCh_kL|morkCh_kU) ) + +/* is alphanumeric, including lower case, upper case, and digits */ +#define morkCh_IsAlphaNum(c) \ + (morkCh_Type[ (mork_ch)(c) ]&(morkCh_kL|morkCh_kU|morkCh_kD)) + +/* ````` repeated testing of predicate bits in single flag byte ````` */ + +#define morkCh_GetFlags(c) ( morkCh_Type[ (mork_ch)(c) ] ) + +#define morkFlags_IsDigit(f) ( (f) & morkCh_kD ) +#define morkFlags_IsHex(f) ( (f) & morkCh_kX ) +#define morkFlags_IsValue(f) ( (f) & morkCh_kV ) +#define morkFlags_IsWhite(f) ( (f) & morkCh_kW ) +#define morkFlags_IsName(f) ( (f) & morkCh_kN ) +#define morkFlags_IsMore(f) ( (f) & morkCh_kM ) +#define morkFlags_IsAlpha(f) ( (f) & (morkCh_kL|morkCh_kU) ) +#define morkFlags_IsAlphaNum(f) ( (f) & (morkCh_kL|morkCh_kU|morkCh_kD) ) + +#define morkFlags_IsUpper(f) ( (f) & morkCh_kU ) +#define morkFlags_IsLower(f) ( (f) & morkCh_kL ) + +/* ````` character case (e.g. for case insensitive operations) ````` */ + + +#define morkCh_IsAscii(c) ( ((mork_u1) c) <= 0x7F ) +#define morkCh_IsSevenBitChar(c) ( ((mork_u1) c) <= 0x7F ) + +/* ````` character case (e.g. for case insensitive operations) ````` */ + +#define morkCh_ToLower(c) ((c)-'A'+'a') +#define morkCh_ToUpper(c) ((c)-'a'+'A') + +/* extern int morkCh_IsUpper (int c); */ +#define morkCh_IsUpper(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kU ) + +/* extern int morkCh_IsLower (int c); */ +#define morkCh_IsLower(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kL ) + +#endif +/* _MORKCH_ */ diff --git a/db/mork/src/morkConfig.cpp b/db/mork/src/morkConfig.cpp new file mode 100644 index 0000000000..b58cd03c8e --- /dev/null +++ b/db/mork/src/morkConfig.cpp @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCONFIG_ +#include "morkConfig.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +void mork_assertion_signal(const char* inMessage) +{ +#if defined(MORK_WIN) || defined(MORK_MAC) + // asm { int 3 } + NS_ERROR(inMessage); +#endif /*MORK_WIN*/ +} + +#ifdef MORK_PROVIDE_STDLIB + +MORK_LIB_IMPL(mork_i4) +mork_memcmp(const void* inOne, const void* inTwo, mork_size inSize) +{ + const mork_u1* t = (const mork_u1*) inTwo; + const mork_u1* s = (const mork_u1*) inOne; + const mork_u1* end = s + inSize; + mork_i4 delta; + + while ( s < end ) + { + delta = ((mork_i4) *s) - ((mork_i4) *t); + if ( delta ) + return delta; + else + { + ++t; + ++s; + } + } + return 0; +} + +MORK_LIB_IMPL(void) +mork_memcpy(void* outDst, const void* inSrc, mork_size inSize) +{ + mork_u1* d = (mork_u1*) outDst; + mork_u1* end = d + inSize; + const mork_u1* s = ((const mork_u1*) inSrc); + + while ( inSize >= 8 ) + { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + inSize -= 8; + } + + while ( d < end ) + *d++ = *s++; +} + +MORK_LIB_IMPL(void) +mork_memmove(void* outDst, const void* inSrc, mork_size inSize) +{ + mork_u1* d = (mork_u1*) outDst; + const mork_u1* s = (const mork_u1*) inSrc; + if ( d != s && inSize ) // copy is necessary? + { + const mork_u1* srcEnd = s + inSize; // one past last source byte + + if ( d > s && d < srcEnd ) // overlap? need to copy backwards? + { + s = srcEnd; // start one past last source byte + d += inSize; // start one past last dest byte + mork_u1* dstBegin = d; // last byte to write is first in dest range + while ( d - dstBegin >= 8 ) + { + *--d = *--s; + *--d = *--s; + *--d = *--s; + *--d = *--s; + + *--d = *--s; + *--d = *--s; + *--d = *--s; + *--d = *--s; + } + while ( d > dstBegin ) + *--d = *--s; + } + else // can copy forwards without any overlap + { + mork_u1* dstEnd = d + inSize; + while ( dstEnd - d >= 8 ) + { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + while ( d < dstEnd ) + *d++ = *s++; + } + } +} + +MORK_LIB_IMPL(void) +mork_memset(void* outDst, int inByte, mork_size inSize) +{ + mork_u1* d = (mork_u1*) outDst; + mork_u1* end = d + inSize; + while ( d < end ) + *d++ = (mork_u1) inByte; +} + +MORK_LIB_IMPL(void) +mork_strcpy(void* outDst, const void* inSrc) +{ + // back up one first to support preincrement + mork_u1* d = ((mork_u1*) outDst) - 1; + const mork_u1* s = ((const mork_u1*) inSrc) - 1; + while ( ( *++d = *++s ) != 0 ) + /* empty */; +} + +MORK_LIB_IMPL(mork_i4) +mork_strcmp(const void* inOne, const void* inTwo) +{ + const mork_u1* t = (const mork_u1*) inTwo; + const mork_u1* s = ((const mork_u1*) inOne); + mork_i4 a; + mork_i4 b; + mork_i4 delta; + + do + { + a = (mork_i4) *s++; + b = (mork_i4) *t++; + delta = a - b; + } + while ( !delta && a && b ); + + return delta; +} + +MORK_LIB_IMPL(mork_i4) +mork_strncmp(const void* inOne, const void* inTwo, mork_size inSize) +{ + const mork_u1* t = (const mork_u1*) inTwo; + const mork_u1* s = (const mork_u1*) inOne; + const mork_u1* end = s + inSize; + mork_i4 delta; + mork_i4 a; + mork_i4 b; + + while ( s < end ) + { + a = (mork_i4) *s++; + b = (mork_i4) *t++; + delta = a - b; + if ( delta || !a || !b ) + return delta; + } + return 0; +} + +MORK_LIB_IMPL(mork_size) +mork_strlen(const void* inString) +{ + // back up one first to support preincrement + const mork_u1* s = ((const mork_u1*) inString) - 1; + while ( *++s ) // preincrement is cheapest + /* empty */; + + return s - ((const mork_u1*) inString); // distance from original address +} + +#endif /*MORK_PROVIDE_STDLIB*/ + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkConfig.h b/db/mork/src/morkConfig.h new file mode 100644 index 0000000000..370cf17139 --- /dev/null +++ b/db/mork/src/morkConfig.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKCONFIG_ +#define _MORKCONFIG_ 1 + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { %%%%% begin debug mode options in Mork %%%%% +#define MORK_DEBUG 1 +// } %%%%% end debug mode options in Mork %%%%% + +#ifdef MORK_DEBUG +#define MORK_MAX_CODE_COMPILE 1 +#endif + +// { %%%%% begin platform defs peculiar to Mork %%%%% + +#ifdef XP_MACOSX +#define MORK_MAC 1 +#endif + +#ifdef XP_WIN +#define MORK_WIN 1 +#endif + +#ifdef XP_UNIX +#define MORK_UNIX 1 +#endif + +// } %%%%% end platform defs peculiar to Mork %%%%% + +#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) +#include +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include /* for SEEK_SET, SEEK_END */ +#endif + +#include "nsDebug.h" + +#define MORK_ISPRINT(c) isprint(c) + +#define MORK_FILETELL(file) ftell(file) +#define MORK_FILESEEK(file, where, how) fseek(file, where, how) +#define MORK_FILEREAD(outbuf, insize, file) fread(outbuf, 1, insize, file) +#if defined(MORK_WIN) +void mork_fileflush(FILE * file); +#define MORK_FILEFLUSH(file) mork_fileflush(file) +#else +#define MORK_FILEFLUSH(file) fflush(file) +#endif /*MORK_WIN*/ + +#define MORK_FILEOPEN(file, how) fopen(file, how) +#define MORK_FILECLOSE(file) fclose(file) +#endif /*MORK_WIN*/ + +/* ===== separating switchable features ===== */ + +#define MORK_ENABLE_ZONE_ARENAS 1 /* using morkZone for pooling */ + +//#define MORK_ENABLE_PROBE_MAPS 1 /* use smaller hash tables */ + +#define MORK_BEAD_OVER_NODE_MAPS 1 /* use bead not node maps */ + +/* ===== pooling ===== */ + +#if defined(HAVE_64BIT_BUILD) +#define MORK_CONFIG_ALIGN_8 1 /* must have 8 byte alignment */ +#else +#define MORK_CONFIG_PTR_SIZE_4 1 /* sizeof(void*) == 4 */ +#endif + +// #define MORK_DEBUG_HEAP_STATS 1 /* analyze per-block heap usage */ + +/* ===== ===== ===== ===== line characters ===== ===== ===== ===== */ +#define mork_kCR 0x0D +#define mork_kLF 0x0A +#define mork_kVTAB '\013' +#define mork_kFF '\014' +#define mork_kTAB '\011' +#define mork_kCRLF "\015\012" /* A CR LF equivalent string */ + +#if defined(MORK_MAC) +# define mork_kNewline "\015" +# define mork_kNewlineSize 1 +#else +# if defined(MORK_WIN) +# define mork_kNewline "\015\012" +# define mork_kNewlineSize 2 +# else +# if defined(MORK_UNIX) +# define mork_kNewline "\012" +# define mork_kNewlineSize 1 +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ + +// { %%%%% begin assertion macro %%%%% +extern void mork_assertion_signal(const char* inMessage); +#define MORK_ASSERTION_SIGNAL(Y) mork_assertion_signal(Y) +#define MORK_ASSERT(X) if (!(X)) MORK_ASSERTION_SIGNAL(#X) +// } %%%%% end assertion macro %%%%% + +#define MORK_LIB(return) return /*API return declaration*/ +#define MORK_LIB_IMPL(return) return /*implementation return declaration*/ + +// { %%%%% begin standard c utility methods %%%%% + +#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC) +#define MORK_USE_C_STDLIB 1 +#endif /*MORK_WIN*/ + +#ifdef MORK_USE_C_STDLIB +#define MORK_MEMCMP(src1,src2,size) memcmp(src1,src2,size) +#define MORK_MEMCPY(dest,src,size) memcpy(dest,src,size) +#define MORK_MEMMOVE(dest,src,size) memmove(dest,src,size) +#define MORK_MEMSET(dest,byte,size) memset(dest,byte,size) +#define MORK_STRCPY(dest,src) strcpy(dest,src) +#define MORK_STRCMP(one,two) strcmp(one,two) +#define MORK_STRNCMP(one,two,length) strncmp(one,two,length) +#define MORK_STRLEN(string) strlen(string) +#endif /*MORK_USE_C_STDLIB*/ + +#ifdef MORK_PROVIDE_STDLIB +MORK_LIB(mork_i4) mork_memcmp(const void* a, const void* b, mork_size inSize); +MORK_LIB(void) mork_memcpy(void* dst, const void* src, mork_size inSize); +MORK_LIB(void) mork_memmove(void* dst, const void* src, mork_size inSize); +MORK_LIB(void) mork_memset(void* dst, int inByte, mork_size inSize); +MORK_LIB(void) mork_strcpy(void* dst, const void* src); +MORK_LIB(mork_i4) mork_strcmp(const void* a, const void* b); +MORK_LIB(mork_i4) mork_strncmp(const void* a, const void* b, mork_size inSize); +MORK_LIB(mork_size) mork_strlen(const void* inString); + +#define MORK_MEMCMP(src1,src2,size) mork_memcmp(src1,src2,size) +#define MORK_MEMCPY(dest,src,size) mork_memcpy(dest,src,size) +#define MORK_MEMMOVE(dest,src,size) mork_memmove(dest,src,size) +#define MORK_MEMSET(dest,byte,size) mork_memset(dest,byte,size) +#define MORK_STRCPY(dest,src) mork_strcpy(dest,src) +#define MORK_STRCMP(one,two) mork_strcmp(one,two) +#define MORK_STRNCMP(one,two,length) mork_strncmp(one,two,length) +#define MORK_STRLEN(string) mork_strlen(string) +#endif /*MORK_PROVIDE_STDLIB*/ + +// } %%%%% end standard c utility methods %%%%% + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCONFIG_ */ + diff --git a/db/mork/src/morkCursor.cpp b/db/mork/src/morkCursor.cpp new file mode 100644 index 0000000000..f6aa59297e --- /dev/null +++ b/db/mork/src/morkCursor.cpp @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkCursor::CloseMorkNode(morkEnv* ev) // CloseCursor() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkCursor::~morkCursor() // assert CloseCursor() executed earlier +{ +} + +/*public non-poly*/ +morkCursor::morkCursor(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mCursor_Seed( 0 ) +, mCursor_Pos( -1 ) +, mCursor_DoFailOnSeedOutOfSync( morkBool_kFalse ) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kCursor; +} + +NS_IMPL_ISUPPORTS_INHERITED(morkCursor, morkObject, nsIMdbCursor) + +/*public non-poly*/ void +morkCursor::CloseCursor(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mCursor_Seed = 0; + mCursor_Pos = -1; + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkCursor::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) +{ + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkCursor::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) +{ + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkCursor::AddWeakRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::AddWeakRef((morkEnv *) mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkCursor::AddStrongRef(morkEnv* mev) +{ + return morkNode::AddStrongRef(mev); +} +#endif + +NS_IMETHODIMP_(mork_uses) +morkCursor::AddStrongRef(nsIMdbEnv* mev) +{ + return morkNode::AddStrongRef((morkEnv *) mev); +} + +NS_IMETHODIMP +morkCursor::CutWeakRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::CutWeakRef((morkEnv *) mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkCursor::CutStrongRef(morkEnv* mev) +{ + return morkNode::CutStrongRef(mev); +} +#endif + +NS_IMETHODIMP +morkCursor::CutStrongRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_uses to nsresult + return static_cast(morkNode::CutStrongRef((morkEnv *) mev)); +} + +NS_IMETHODIMP +morkCursor::CloseMdbObject(nsIMdbEnv* mev) +{ + return morkNode::CloseMdbObject((morkEnv *) mev); +} + +NS_IMETHODIMP +morkCursor::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) +{ + *outOpen = IsOpenNode(); + return NS_OK; +} +NS_IMETHODIMP +morkCursor::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly) +{ + *outIsReadonly = IsFrozen(); + return NS_OK; +} +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +NS_IMETHODIMP +morkCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkCursor.h b/db/mork/src/morkCursor.h new file mode 100644 index 0000000000..5b3518f95c --- /dev/null +++ b/db/mork/src/morkCursor.h @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKCURSOR_ +#define _MORKCURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kCursor /*i*/ 0x4375 /* ascii 'Cu' */ + +class morkCursor : public morkObject, public nsIMdbCursor{ // collection iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin ref counting for well-behaved cyclic graphs ----- + NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs + mdb_count* outCount) override; + NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs + mdb_count* outCount) override; + + NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero + NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override; + // } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + +// { ===== begin nsIMdbCursor methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) override; + // } ----- end attribute methods ----- + +// } ===== end nsIMdbCursor methods ===== + + // } ----- end attribute methods ----- + + mork_seed mCursor_Seed; + mork_pos mCursor_Pos; + mork_bool mCursor_DoFailOnSeedOutOfSync; + mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseCursor() only if open + +public: // morkCursor construction & destruction + morkCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseCursor(morkEnv* ev); // called by CloseMorkNode(); + +protected: + virtual ~morkCursor(); // assert that CloseCursor() executed earlier + +private: // copying is not allowed + morkCursor(const morkCursor& other); + morkCursor& operator=(const morkCursor& other); + +public: // dynamic type identification + mork_bool IsCursor() const + { return IsNode() && mNode_Derived == morkDerived_kCursor; } +// } ===== end morkNode methods ===== + +public: // other cursor methods + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakCursor(morkCursor* me, + morkEnv* ev, morkCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongCursor(morkCursor* me, + morkEnv* ev, morkCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKCURSOR_ */ diff --git a/db/mork/src/morkDeque.cpp b/db/mork/src/morkDeque.cpp new file mode 100644 index 0000000000..3a380b5d86 --- /dev/null +++ b/db/mork/src/morkDeque.cpp @@ -0,0 +1,288 @@ +/************************************************************************* +This software is part of a public domain IronDoc source code distribution, +and is provided on an "AS IS" basis, with all risks borne by the consumers +or users of the IronDoc software. There are no warranties, guarantees, or +promises about quality of any kind; and no remedies for failure exist. + +Permission is hereby granted to use this IronDoc software for any purpose +at all, without need for written agreements, without royalty or license +fees, and without fees or obligations of any other kind. Anyone can use, +copy, change and distribute this software for any purpose, and nothing is +required, implicitly or otherwise, in exchange for this usage. + +You cannot apply your own copyright to this software, but otherwise you +are encouraged to enjoy the use of this software in any way you see fit. +However, it would be rude to remove names of developers from the code. +(IronDoc is also known by the short name "Fe" and a longer name "Ferrum", +which are used interchangeably with the name IronDoc in the sources.) +*************************************************************************/ +/* + * File: morkDeque.cpp + * Contains: Ferrum deque (double ended queue (linked list)) + * + * Copied directly from public domain IronDoc, with minor naming tweaks: + * Designed and written by David McCusker, but all this code is public domain. + * There are no warranties, no guarantees, no promises, and no remedies. + */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +/*============================================================================= + * morkNext: linked list node for very simple, singly-linked list + */ + +morkNext::morkNext() : mNext_Link( 0 ) +{ +} + +/*static*/ void* +morkNext::MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) +{ + void* next = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &next); + if ( !next ) + ev->OutOfMemoryError(); + + return next; +} + +/*static*/ +void morkNext::ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap) +{ + if ( ioHeap ) + { + ioHeap->Free(ev->AsMdbEnv(), this); + } + else + ev->NilPointerError(); +} + +/*============================================================================= + * morkList: simple, singly-linked list + */ + +morkList::morkList() : mList_Head( 0 ), mList_Tail( 0 ) +{ +} + +void morkList::CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap) +// make empty list, zapping every member by calling ZapOldNext() +{ + if ( ioHeap ) + { + morkNext* next = 0; + while ( (next = this->PopHead()) != 0 ) + next->ZapOldNext(ev, ioHeap); + + mList_Head = 0; + mList_Tail = 0; + } + else + ev->NilPointerError(); +} + +void morkList::CutAllListMembers() +// just make list empty, dropping members without zapping +{ + while ( this->PopHead() ) + /* empty */; + + mList_Head = 0; + mList_Tail = 0; +} + +morkNext* morkList::PopHead() // cut head of list +{ + morkNext* outHead = mList_Head; + if ( outHead ) // anything to cut from list? + { + morkNext* next = outHead->mNext_Link; + mList_Head = next; + if ( !next ) // cut the last member, so tail no longer exists? + mList_Tail = 0; + + outHead->mNext_Link = 0; // nil outgoing node link; unnecessary, but tidy + } + return outHead; +} + + +void morkList::PushHead(morkNext* ioLink) // add to head of list +{ + morkNext* head = mList_Head; // old head of list + morkNext* tail = mList_Tail; // old tail of list + + MORK_ASSERT( (head && tail) || (!head && !tail)); + + ioLink->mNext_Link = head; // make old head follow the new link + if ( !head ) // list was previously empty? + mList_Tail = ioLink; // head is also tail for first member added + + mList_Head = ioLink; // head of list is the new link +} + +void morkList::PushTail(morkNext* ioLink) // add to tail of list +{ + morkNext* head = mList_Head; // old head of list + morkNext* tail = mList_Tail; // old tail of list + + MORK_ASSERT( (head && tail) || (!head && !tail)); + + ioLink->mNext_Link = 0; + if ( tail ) + { + tail->mNext_Link = ioLink; + mList_Tail = ioLink; + } + else // list was previously empty? + mList_Head = mList_Tail = ioLink; // tail is also head for first member added +} + +/*============================================================================= + * morkLink: linked list node embedded in objs to allow insertion in morkDeques + */ + +morkLink::morkLink() : mLink_Next( 0 ), mLink_Prev( 0 ) +{ +} + +/*static*/ void* +morkLink::MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) +{ + void* alink = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &alink); + if ( !alink ) + ev->OutOfMemoryError(); + + return alink; +} + +/*static*/ +void morkLink::ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap) +{ + if ( ioHeap ) + { + ioHeap->Free(ev->AsMdbEnv(), this); + } + else + ev->NilPointerError(); +} + +/*============================================================================= + * morkDeque: doubly linked list modeled after VAX queue instructions + */ + +morkDeque::morkDeque() +{ + mDeque_Head.SelfRefer(); +} + + +/*| RemoveFirst: +|*/ +morkLink* +morkDeque::RemoveFirst() /*i*/ +{ + morkLink* alink = mDeque_Head.mLink_Next; + if ( alink != &mDeque_Head ) + { + (mDeque_Head.mLink_Next = alink->mLink_Next)->mLink_Prev = + &mDeque_Head; + return alink; + } + return (morkLink*) 0; +} + +/*| RemoveLast: +|*/ +morkLink* +morkDeque::RemoveLast() /*i*/ +{ + morkLink* alink = mDeque_Head.mLink_Prev; + if ( alink != &mDeque_Head ) + { + (mDeque_Head.mLink_Prev = alink->mLink_Prev)->mLink_Next = + &mDeque_Head; + return alink; + } + return (morkLink*) 0; +} + +/*| At: +|*/ +morkLink* +morkDeque::At(mork_pos index) const /*i*/ + /* indexes are one based (and not zero based) */ +{ + mork_num count = 0; + morkLink* alink; + for ( alink = this->First(); alink; alink = this->After(alink) ) + { + if ( ++count == (mork_num) index ) + break; + } + return alink; +} + +/*| IndexOf: +|*/ +mork_pos +morkDeque::IndexOf(const morkLink* member) const /*i*/ + /* indexes are one based (and not zero based) */ + /* zero means member is not in deque */ +{ + mork_num count = 0; + const morkLink* alink; + for ( alink = this->First(); alink; alink = this->After(alink) ) + { + ++count; + if ( member == alink ) + return (mork_pos) count; + } + return 0; +} + +/*| Length: +|*/ +mork_num +morkDeque::Length() const /*i*/ +{ + mork_num count = 0; + morkLink* alink; + for ( alink = this->First(); alink; alink = this->After(alink) ) + ++count; + return count; +} + +/*| LengthCompare: +|*/ +int +morkDeque::LengthCompare(mork_num c) const /*i*/ +{ + mork_num count = 0; + const morkLink* alink; + for ( alink = this->First(); alink; alink = this->After(alink) ) + { + if ( ++count > c ) + return 1; + } + return ( count == c )? 0 : -1; +} diff --git a/db/mork/src/morkDeque.h b/db/mork/src/morkDeque.h new file mode 100644 index 0000000000..4375ecf029 --- /dev/null +++ b/db/mork/src/morkDeque.h @@ -0,0 +1,239 @@ +/************************************************************************* +This software is part of a public domain IronDoc source code distribution, +and is provided on an "AS IS" basis, with all risks borne by the consumers +or users of the IronDoc software. There are no warranties, guarantees, or +promises about quality of any kind; and no remedies for failure exist. + +Permission is hereby granted to use this IronDoc software for any purpose +at all, without need for written agreements, without royalty or license +fees, and without fees or obligations of any other kind. Anyone can use, +copy, change and distribute this software for any purpose, and nothing is +required, implicitly or otherwise, in exchange for this usage. + +You cannot apply your own copyright to this software, but otherwise you +are encouraged to enjoy the use of this software in any way you see fit. +However, it would be rude to remove names of developers from the code. +(IronDoc is also known by the short name "Fe" and a longer name "Ferrum", +which are used interchangeably with the name IronDoc in the sources.) +*************************************************************************/ +/* + * File: morkDeque.h + * Contains: Ferrum deque (double ended queue (linked list)) + * + * Copied directly from public domain IronDoc, with minor naming tweaks: + * Designed and written by David McCusker, but all this code is public domain. + * There are no warranties, no guarantees, no promises, and no remedies. + */ + +#ifndef _MORKDEQUE_ +#define _MORKDEQUE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +/*============================================================================= + * morkNext: linked list node for very simple, singly-linked list + */ + +class morkNext /*d*/ { +public: + morkNext* mNext_Link; + +public: + morkNext(int inZero) : mNext_Link( 0 ) { } + + morkNext(morkNext* ioLink) : mNext_Link( ioLink ) { } + + morkNext(); // mNext_Link( 0 ), { } + +public: + morkNext* GetNextLink() const { return mNext_Link; } + +public: // link memory management methods + static void* MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + void ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap); + +public: // link memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkNext::MakeNewNext(inSize, ioHeap, ev); } + + void operator delete(void* ioAddress) // DO NOT CALL THIS, hope to crash: + { ((morkNext*) 0)->ZapOldNext((morkEnv*) 0, (nsIMdbHeap*) 0); } // boom +}; + +/*============================================================================= + * morkList: simple, singly-linked list + */ + +/*| morkList: a list of singly-linked members (instances of morkNext), where +**| the number of list members might be so numerous that we must about cost +**| for two pointer link slots per member (as happens with morkLink). +**| +**|| morkList is intended to support lists of changes in morkTable, where we +**| are worried about the space cost of representing such changes. (Later we +**| can use an array instead, when we get even more worried, to avoid cost +**| of link slots at all, per member). +**| +**|| Do NOT create cycles in links using this list class, since we do not +**| deal with them very nicely. +|*/ +class morkList /*d*/ { +public: + morkNext* mList_Head; // first link in the list + morkNext* mList_Tail; // last link in the list + +public: + morkNext* GetListHead() const { return mList_Head; } + morkNext* GetListTail() const { return mList_Tail; } + + mork_bool IsListEmpty() const { return ( mList_Head == 0 ); } + mork_bool HasListMembers() const { return ( mList_Head != 0 ); } + +public: + morkList(); // : mList_Head( 0 ), mList_Tail( 0 ) { } + + void CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap); + // make empty list, zapping every member by calling ZapOldNext() + + void CutAllListMembers(); + // just make list empty, dropping members without zapping + +public: + morkNext* PopHead(); // cut head of list + + // Note we don't support PopTail(), so use morkDeque if you need that. + + void PushHead(morkNext* ioLink); // add to head of list + void PushTail(morkNext* ioLink); // add to tail of list +}; + +/*============================================================================= + * morkLink: linked list node embedded in objs to allow insertion in morkDeques + */ + +class morkLink /*d*/ { +public: + morkLink* mLink_Next; + morkLink* mLink_Prev; + +public: + morkLink(int inZero) : mLink_Next( 0 ), mLink_Prev( 0 ) { } + + morkLink(); // mLink_Next( 0 ), mLink_Prev( 0 ) { } + +public: + morkLink* Next() const { return mLink_Next; } + morkLink* Prev() const { return mLink_Prev; } + + void SelfRefer() { mLink_Next = mLink_Prev = this; } + void Clear() { mLink_Next = mLink_Prev = 0; } + + void AddBefore(morkLink* old) + { + ((old)->mLink_Prev->mLink_Next = (this))->mLink_Prev = (old)->mLink_Prev; + ((this)->mLink_Next = (old))->mLink_Prev = this; + } + + void AddAfter(morkLink* old) + { + ((old)->mLink_Next->mLink_Prev = (this))->mLink_Next = (old)->mLink_Next; + ((this)->mLink_Prev = (old))->mLink_Next = this; + } + + void Remove() + { + (mLink_Prev->mLink_Next = mLink_Next)->mLink_Prev = mLink_Prev; + } + +public: // link memory management methods + static void* MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + void ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap); + +public: // link memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkLink::MakeNewLink(inSize, ioHeap, ev); } + +}; + +/*============================================================================= + * morkDeque: doubly linked list modeled after VAX queue instructions + */ + +class morkDeque /*d*/ { +public: + morkLink mDeque_Head; + +public: // construction + morkDeque(); // { mDeque_Head.SelfRefer(); } + +public:// methods + morkLink* RemoveFirst(); + + morkLink* RemoveLast(); + + morkLink* At(mork_pos index) const ; /* one-based, not zero-based */ + + mork_pos IndexOf(const morkLink* inMember) const; + /* one-based index ; zero means member is not in deque */ + + mork_num Length() const; + + /* the following method is more efficient for long lists: */ + int LengthCompare(mork_num inCount) const; + /* -1: length < count, 0: length == count, 1: length > count */ + +public: // inlines + + mork_bool IsEmpty()const + { return (mDeque_Head.mLink_Next == (morkLink*) &mDeque_Head); } + + morkLink* After(const morkLink* old) const + { return (((old)->mLink_Next != &mDeque_Head)? + (old)->mLink_Next : (morkLink*) 0); } + + morkLink* Before(const morkLink* old) const + { return (((old)->mLink_Prev != &mDeque_Head)? + (old)->mLink_Prev : (morkLink*) 0); } + + morkLink* First() const + { return ((mDeque_Head.mLink_Next != &mDeque_Head)? + mDeque_Head.mLink_Next : (morkLink*) 0); } + + morkLink* Last() const + { return ((mDeque_Head.mLink_Prev != &mDeque_Head)? + mDeque_Head.mLink_Prev : (morkLink*) 0); } + +/* +From IronDoc documentation for AddFirst: ++--------+ +--------+ +--------+ +--------+ +--------+ +| h.next |-->| b.next | | h.next |-->| a.next |-->| b.next | ++--------+ +--------+ ==> +--------+ +--------+ +--------+ +| h.prev |<--| b.prev | | h.prev |<--| a.prev |<--| b.prev | ++--------+ +--------+ +--------+ +--------+ +--------+ +*/ + + void AddFirst(morkLink* in) /*i*/ + { + ( (mDeque_Head.mLink_Next->mLink_Prev = + (in))->mLink_Next = mDeque_Head.mLink_Next, + ((in)->mLink_Prev = &mDeque_Head)->mLink_Next = (in) ); + } +/* +From IronDoc documentation for AddLast: ++--------+ +--------+ +--------+ +--------+ +--------+ +| y.next |-->| h.next | | y.next |-->| z.next |-->| h.next | ++--------+ +--------+ ==> +--------+ +--------+ +--------+ +| y.prev |<--| h.prev | | y.prev |<--| z.prev |<--| h.prev | ++--------+ +--------+ +--------+ +--------+ +--------+ +*/ + + void AddLast(morkLink* in) + { + ( (mDeque_Head.mLink_Prev->mLink_Next = + (in))->mLink_Prev = mDeque_Head.mLink_Prev, + ((in)->mLink_Next = &mDeque_Head)->mLink_Prev = (in) ); + } +}; + +#endif /* _MORKDEQUE_ */ diff --git a/db/mork/src/morkEnv.cpp b/db/mork/src/morkEnv.cpp new file mode 100644 index 0000000000..083942f677 --- /dev/null +++ b/db/mork/src/morkEnv.cpp @@ -0,0 +1,615 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKCH_ +#include "morkCh.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +#include "morkFactory.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkEnv::CloseMorkNode(morkEnv* ev) /*i*/ // CloseEnv() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseEnv(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkEnv::~morkEnv() /*i*/ // assert CloseEnv() executed earlier +{ + CloseMorkNode(mMorkEnv); + if (mEnv_Heap) + { + mork_bool ownsHeap = mEnv_OwnsHeap; + nsIMdbHeap*saveHeap = mEnv_Heap; + + if (ownsHeap) + { +#ifdef MORK_DEBUG_HEAP_STATS + printf("%d blocks remaining \n", ((orkinHeap *) saveHeap)->HeapBlockCount()); + mork_u4* array = (mork_u4*) this; + array -= 3; + // null out heap ptr in mem block so we won't crash trying to use it to + // delete the env. + *array = nullptr; +#endif // MORK_DEBUG_HEAP_STATS + // whoops, this is our heap - hmm. Can't delete it, or not allocate env's from + // an orkinHeap. + delete saveHeap; + } + + } +// MORK_ASSERT(mEnv_SelfAsMdbEnv==0); + MORK_ASSERT(mEnv_ErrorHook==0); +} + +/* choose morkBool_kTrue or morkBool_kFalse for kBeVerbose: */ +#define morkEnv_kBeVerbose morkBool_kFalse + +/*public non-poly*/ +morkEnv::morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap) +: morkObject(inUsage, ioHeap, morkColor_kNone) +, mEnv_Factory( ioFactory ) +, mEnv_Heap( ioSlotHeap ) + +, mEnv_SelfAsMdbEnv( 0 ) +, mEnv_ErrorHook( 0 ) +, mEnv_HandlePool( 0 ) + +, mEnv_ErrorCount( 0 ) +, mEnv_WarningCount( 0 ) + +, mEnv_ErrorCode(NS_OK) + +, mEnv_DoTrace( morkBool_kFalse ) +, mEnv_AutoClear( morkAble_kDisabled ) +, mEnv_ShouldAbort( morkBool_kFalse ) +, mEnv_BeVerbose( morkEnv_kBeVerbose ) +, mEnv_OwnsHeap ( morkBool_kFalse ) +{ + MORK_ASSERT(ioSlotHeap && ioFactory ); + if ( ioSlotHeap ) + { + // mEnv_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, this, &mEnv_Heap); + + mEnv_HandlePool = new morkPool(morkUsage::kGlobal, + (nsIMdbHeap*) 0, ioSlotHeap); + + MORK_ASSERT(mEnv_HandlePool); + if ( mEnv_HandlePool && this->Good() ) + { + mNode_Derived = morkDerived_kEnv; + mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + } + } +} + +/*public non-poly*/ +morkEnv::morkEnv(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbEnv* inSelfAsMdbEnv, + morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mEnv_Factory( ioFactory ) +, mEnv_Heap( ioSlotHeap ) + +, mEnv_SelfAsMdbEnv( inSelfAsMdbEnv ) +, mEnv_ErrorHook( 0 ) +, mEnv_HandlePool( 0 ) + +, mEnv_ErrorCount( 0 ) +, mEnv_WarningCount( 0 ) + +, mEnv_ErrorCode(NS_OK) + +, mEnv_DoTrace( morkBool_kFalse ) +, mEnv_AutoClear( morkAble_kDisabled ) +, mEnv_ShouldAbort( morkBool_kFalse ) +, mEnv_BeVerbose( morkEnv_kBeVerbose ) +, mEnv_OwnsHeap ( morkBool_kFalse ) +{ + // $$$ do we need to refcount the inSelfAsMdbEnv nsIMdbEnv?? + + if ( ioFactory && inSelfAsMdbEnv && ioSlotHeap) + { + // mEnv_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mEnv_Heap); + + mEnv_HandlePool = new(*ioSlotHeap, ev) morkPool(ev, + morkUsage::kHeap, ioSlotHeap, ioSlotHeap); + + MORK_ASSERT(mEnv_HandlePool); + if ( mEnv_HandlePool && ev->Good() ) + { + mNode_Derived = morkDerived_kEnv; + mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + } + } + else + ev->NilPointerError(); +} + +NS_IMPL_ISUPPORTS_INHERITED(morkEnv, morkObject, nsIMdbEnv) +/*public non-poly*/ void +morkEnv::CloseEnv(morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + // $$$ release mEnv_SelfAsMdbEnv?? + // $$$ release mEnv_ErrorHook?? + + mEnv_SelfAsMdbEnv = 0; + mEnv_ErrorHook = 0; + + morkPool* savePool = mEnv_HandlePool; + morkPool::SlotStrongPool((morkPool*) 0, ev, &mEnv_HandlePool); + // free the pool + if (mEnv_SelfAsMdbEnv) + { + if (savePool && mEnv_Heap) + mEnv_Heap->Free(this->AsMdbEnv(), savePool); + } + else + { + if (savePool) + { + if (savePool->IsOpenNode()) + savePool->CloseMorkNode(ev); + delete savePool; + } + // how do we free this? might need to get rid of asserts. + } + // mEnv_Factory is NOT refcounted + + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_size +morkEnv::OidAsHex(void* outBuf, const mdbOid& inOid) +// sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope); +{ + mork_u1* p = (mork_u1*) outBuf; + mork_size outSize = this->TokenAsHex(p, inOid.mOid_Id); + p += outSize; + *p++ = ':'; + + mork_scope scope = inOid.mOid_Scope; + if ( scope < 0x80 && morkCh_IsName((mork_ch) scope) ) + { + *p++ = (mork_u1) scope; + *p = 0; // null termination + outSize += 2; + } + else + { + *p++ = '^'; + mork_size scopeSize = this->TokenAsHex(p, scope); + outSize += scopeSize + 2; + } + return outSize; +} + + +mork_u1 +morkEnv::HexToByte(mork_ch inFirstHex, mork_ch inSecondHex) +{ + mork_u1 hi = 0; // high four hex bits + mork_flags f = morkCh_GetFlags(inFirstHex); + if ( morkFlags_IsDigit(f) ) + hi = (mork_u1) (inFirstHex - (mork_ch) '0'); + else if ( morkFlags_IsUpper(f) ) + hi = (mork_u1) ((inFirstHex - (mork_ch) 'A') + 10); + else if ( morkFlags_IsLower(f) ) + hi = (mork_u1) ((inFirstHex - (mork_ch) 'a') + 10); + + mork_u1 lo = 0; // low four hex bits + f = morkCh_GetFlags(inSecondHex); + if ( morkFlags_IsDigit(f) ) + lo = (mork_u1) (inSecondHex - (mork_ch) '0'); + else if ( morkFlags_IsUpper(f) ) + lo = (mork_u1) ((inSecondHex - (mork_ch) 'A') + 10); + else if ( morkFlags_IsLower(f) ) + lo = (mork_u1) ((inSecondHex - (mork_ch) 'a') + 10); + + return (mork_u1) ((hi << 4) | lo); +} + +mork_size +morkEnv::TokenAsHex(void* outBuf, mork_token inToken) + // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken); +{ + static const char morkEnv_kHexDigits[] = "0123456789ABCDEF"; + char* p = (char*) outBuf; + char* end = p + 32; // write no more than 32 digits for safety + if ( inToken ) + { + // first write all the hex digits in backwards order: + while ( p < end && inToken ) // more digits to write? + { + *p++ = morkEnv_kHexDigits[ inToken & 0x0F ]; // low four bits + inToken >>= 4; // we fervently hope this does not sign extend + } + *p = 0; // end the string with a null byte + char* s = (char*) outBuf; // first byte in string + mork_size size = (mork_size) (p - s); // distance from start + + // now reverse the string in place: + // note that p starts on the null byte, so we need predecrement: + while ( --p > s ) // need to swap another byte in the string? + { + char c = *p; // temp for swap + *p = *s; + *s++ = c; // move s forward here, and p backward in the test + } + return size; + } + else // special case for zero integer + { + *p++ = '0'; // write a zero digit + *p = 0; // end with a null byte + return 1; // one digit in hex representation + } +} + +void +morkEnv::StringToYarn(const char* inString, mdbYarn* outYarn) +{ + if ( outYarn ) + { + mdb_fill fill = ( inString )? (mdb_fill) MORK_STRLEN(inString) : 0; + + if ( fill ) // have nonempty content? + { + mdb_size size = outYarn->mYarn_Size; // max dest size + if ( fill > size ) // too much string content? + { + outYarn->mYarn_More = fill - size; // extra string bytes omitted + fill = size; // copy no more bytes than size of yarn buffer + } + void* dest = outYarn->mYarn_Buf; // where bytes are going + if ( !dest ) // nil destination address buffer? + fill = 0; // we can't write any content at all + + if ( fill ) // anything to copy? + MORK_MEMCPY(dest, inString, fill); // copy fill bytes to yarn + + outYarn->mYarn_Fill = fill; // tell yarn size of copied content + } + else // no content to put into the yarn + { + outYarn->mYarn_Fill = 0; // tell yarn that string has no bytes + } + outYarn->mYarn_Form = 0; // always update the form slot + } + else + this->NilPointerError(); +} + +char* +morkEnv::CopyString(nsIMdbHeap* ioHeap, const char* inString) +{ + char* outString = 0; + if ( ioHeap && inString ) + { + mork_size size = MORK_STRLEN(inString) + 1; + ioHeap->Alloc(this->AsMdbEnv(), size, (void**) &outString); + if ( outString ) + MORK_STRCPY(outString, inString); + } + else + this->NilPointerError(); + return outString; +} + +void +morkEnv::FreeString(nsIMdbHeap* ioHeap, char* ioString) +{ + if ( ioHeap ) + { + if ( ioString ) + ioHeap->Free(this->AsMdbEnv(), ioString); + } + else + this->NilPointerError(); +} + +void +morkEnv::NewError(const char* inString) +{ + MORK_ASSERT(morkBool_kFalse); // get developer's attention + + ++mEnv_ErrorCount; + mEnv_ErrorCode = NS_ERROR_FAILURE; + + if ( mEnv_ErrorHook ) + mEnv_ErrorHook->OnErrorString(this->AsMdbEnv(), inString); +} + +void +morkEnv::NewWarning(const char* inString) +{ + MORK_ASSERT(morkBool_kFalse); // get developer's attention + + ++mEnv_WarningCount; + if ( mEnv_ErrorHook ) + mEnv_ErrorHook->OnWarningString(this->AsMdbEnv(), inString); +} + +void +morkEnv::StubMethodOnlyError() +{ + this->NewError("method is stub only"); +} + +void +morkEnv::OutOfMemoryError() +{ + this->NewError("out of memory"); +} + +void +morkEnv::CantMakeWhenBadError() +{ + this->NewError("can't make an object when ev->Bad()"); +} + +static const char morkEnv_kNilPointer[] = "nil pointer"; + +void +morkEnv::NilPointerError() +{ + this->NewError(morkEnv_kNilPointer); +} + +void +morkEnv::NilPointerWarning() +{ + this->NewWarning(morkEnv_kNilPointer); +} + +void +morkEnv::NewNonEnvError() +{ + this->NewError("non-env instance"); +} + +void +morkEnv::NilEnvSlotError() +{ + if ( !mEnv_HandlePool || !mEnv_Factory ) + { + if ( !mEnv_HandlePool ) + this->NewError("nil mEnv_HandlePool"); + if ( !mEnv_Factory ) + this->NewError("nil mEnv_Factory"); + } + else + this->NewError("unknown nil env slot"); +} + + +void morkEnv::NonEnvTypeError(morkEnv* ev) +{ + ev->NewError("non morkEnv"); +} + +void +morkEnv::ClearMorkErrorsAndWarnings() +{ + mEnv_ErrorCount = 0; + mEnv_WarningCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; +} + +void +morkEnv::AutoClearMorkErrorsAndWarnings() +{ + if ( this->DoAutoClear() ) + { + mEnv_ErrorCount = 0; + mEnv_WarningCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; + } +} + +/*static*/ morkEnv* +morkEnv::FromMdbEnv(nsIMdbEnv* ioEnv) // dynamic type checking +{ + morkEnv* outEnv = 0; + if ( ioEnv ) + { + // Note this cast is expected to perform some address adjustment of the + // pointer, so oenv likely does not equal ioEnv. Do not cast to void* + // first to force an exactly equal pointer (we tried it and it's wrong). + morkEnv* ev = (morkEnv*) ioEnv; + if ( ev && ev->IsEnv() ) + { + if ( ev->DoAutoClear() ) + { + ev->mEnv_ErrorCount = 0; + ev->mEnv_WarningCount = 0; + ev->mEnv_ErrorCode = NS_OK; + } + outEnv = ev; + } + else + MORK_ASSERT(outEnv); + } + else + MORK_ASSERT(outEnv); + return outEnv; +} + + +NS_IMETHODIMP +morkEnv::GetErrorCount(mdb_count* outCount, + mdb_bool* outShouldAbort) +{ + if ( outCount ) + *outCount = mEnv_ErrorCount; + if ( outShouldAbort ) + *outShouldAbort = mEnv_ShouldAbort; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetWarningCount(mdb_count* outCount, + mdb_bool* outShouldAbort) +{ + if ( outCount ) + *outCount = mEnv_WarningCount; + if ( outShouldAbort ) + *outShouldAbort = mEnv_ShouldAbort; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetEnvBeVerbose(mdb_bool* outBeVerbose) +{ + NS_ENSURE_ARG_POINTER(outBeVerbose); + *outBeVerbose = mEnv_BeVerbose; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetEnvBeVerbose(mdb_bool inBeVerbose) +{ + mEnv_BeVerbose = inBeVerbose; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetDoTrace(mdb_bool* outDoTrace) +{ + NS_ENSURE_ARG_POINTER(outDoTrace); + *outDoTrace = mEnv_DoTrace; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetDoTrace(mdb_bool inDoTrace) +{ + mEnv_DoTrace = inDoTrace; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetAutoClear(mdb_bool* outAutoClear) +{ + NS_ENSURE_ARG_POINTER(outAutoClear); + *outAutoClear = DoAutoClear(); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetAutoClear(mdb_bool inAutoClear) +{ + if ( inAutoClear ) + EnableAutoClear(); + else + DisableAutoClear(); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetErrorHook(nsIMdbErrorHook** acqErrorHook) +{ + NS_ENSURE_ARG_POINTER(acqErrorHook); + *acqErrorHook = mEnv_ErrorHook; + NS_IF_ADDREF(mEnv_ErrorHook); + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetErrorHook( + nsIMdbErrorHook* ioErrorHook) // becomes referenced +{ + mEnv_ErrorHook = ioErrorHook; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::GetHeap(nsIMdbHeap** acqHeap) +{ + NS_ENSURE_ARG_POINTER(acqHeap); + nsIMdbHeap* outHeap = mEnv_Heap; + + if ( acqHeap ) + *acqHeap = outHeap; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::SetHeap( + nsIMdbHeap* ioHeap) // becomes referenced +{ + nsIMdbHeap_SlotStrongHeap(ioHeap, this, &mEnv_Heap); + return NS_OK; +} +// } ----- end attribute methods ----- + +NS_IMETHODIMP +morkEnv::ClearErrors() // clear errors beore re-entering db API +{ + mEnv_ErrorCount = 0; + mEnv_ErrorCode = NS_OK; + mEnv_ShouldAbort = morkBool_kFalse; + + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::ClearWarnings() // clear warning +{ + mEnv_WarningCount = 0; + return NS_OK; +} + +NS_IMETHODIMP +morkEnv::ClearErrorsAndWarnings() // clear both errors & warnings +{ + ClearMorkErrorsAndWarnings(); + return NS_OK; +} +// } ===== end nsIMdbEnv methods ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkEnv.h b/db/mork/src/morkEnv.h new file mode 100644 index 0000000000..827a56d72a --- /dev/null +++ b/db/mork/src/morkEnv.h @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKENV_ +#define _MORKENV_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +// sean was here +#include "nsError.h" + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kEnv /*i*/ 0x4576 /* ascii 'Ev' */ + +// use NS error codes to make Mork easier to use with the rest of mozilla +#define morkEnv_kNoError NS_SUCCEEDED /* no error has happened */ +#define morkEnv_kNonEnvTypeError NS_ERROR_FAILURE /* morkEnv::IsEnv() is false */ + +#define morkEnv_kStubMethodOnlyError NS_ERROR_NO_INTERFACE +#define morkEnv_kOutOfMemoryError NS_ERROR_OUT_OF_MEMORY +#define morkEnv_kNilPointerError NS_ERROR_NULL_POINTER +#define morkEnv_kNewNonEnvError NS_ERROR_FAILURE +#define morkEnv_kNilEnvSlotError NS_ERROR_FAILURE + +#define morkEnv_kBadFactoryError NS_ERROR_FACTORY_NOT_LOADED +#define morkEnv_kBadFactoryEnvError NS_ERROR_FACTORY_NOT_LOADED +#define morkEnv_kBadEnvError NS_ERROR_FAILURE + +#define morkEnv_kNonHandleTypeError NS_ERROR_FAILURE +#define morkEnv_kNonOpenNodeError NS_ERROR_FAILURE + + +#define morkEnv_kWeakRefCountEnvBonus 0 /* try NOT to leak all env instances */ + +/*| morkEnv: +|*/ +class morkEnv : public morkObject, public nsIMdbEnv { + NS_DECL_ISUPPORTS_INHERITED + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // state is public because the entire Mork system is private + + morkFactory* mEnv_Factory; // NON-refcounted factory + nsIMdbHeap* mEnv_Heap; // NON-refcounted heap + + nsIMdbEnv* mEnv_SelfAsMdbEnv; + nsIMdbErrorHook* mEnv_ErrorHook; + + morkPool* mEnv_HandlePool; // pool for re-using handles + + mork_u2 mEnv_ErrorCount; + mork_u2 mEnv_WarningCount; + + nsresult mEnv_ErrorCode; + + mork_bool mEnv_DoTrace; + mork_able mEnv_AutoClear; + mork_bool mEnv_ShouldAbort; + mork_bool mEnv_BeVerbose; + mork_bool mEnv_OwnsHeap; + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseEnv() only if open + virtual ~morkEnv(); // assert that CloseEnv() executed earlier + + // { ----- begin attribute methods ----- + NS_IMETHOD GetErrorCount(mdb_count* outCount, + mdb_bool* outShouldAbort) override; + NS_IMETHOD GetWarningCount(mdb_count* outCount, + mdb_bool* outShouldAbort) override; + + NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) override; + NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) override; + + NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) override; + NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) override; + + NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) override; + NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) override; + + NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) override; + NS_IMETHOD SetErrorHook( + nsIMdbErrorHook* ioErrorHook) override; // becomes referenced + + NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) override; + NS_IMETHOD SetHeap(nsIMdbHeap* ioHeap) override; // becomes referenced + // } ----- end attribute methods ----- + + NS_IMETHOD ClearErrors() override; // clear errors beore re-entering db API + NS_IMETHOD ClearWarnings() override; // clear warnings + NS_IMETHOD ClearErrorsAndWarnings() override; // clear both errors & warnings +// } ===== end nsIMdbEnv methods ===== +public: // morkEnv construction & destruction + morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap); + morkEnv(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbEnv* inSelfAsMdbEnv, morkFactory* ioFactory, + nsIMdbHeap* ioSlotHeap); + void CloseEnv(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkEnv(const morkEnv& other); + morkEnv& operator=(const morkEnv& other); + +public: // dynamic type identification + mork_bool IsEnv() const + { return IsNode() && mNode_Derived == morkDerived_kEnv; } +// } ===== end morkNode methods ===== + +public: // utility env methods + + mork_u1 HexToByte(mork_ch inFirstHex, mork_ch inSecondHex); + + mork_size TokenAsHex(void* outBuf, mork_token inToken); + // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken); + + mork_size OidAsHex(void* outBuf, const mdbOid& inOid); + // sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope); + + char* CopyString(nsIMdbHeap* ioHeap, const char* inString); + void FreeString(nsIMdbHeap* ioHeap, char* ioString); + void StringToYarn(const char* inString, mdbYarn* outYarn); + +public: // other env methods + + morkHandleFace* NewHandle(mork_size inSize) + { return mEnv_HandlePool->NewHandle(this, inSize, (morkZone*) 0); } + + void ZapHandle(morkHandleFace* ioHandle) + { mEnv_HandlePool->ZapHandle(this, ioHandle); } + + void EnableAutoClear() { mEnv_AutoClear = morkAble_kEnabled; } + void DisableAutoClear() { mEnv_AutoClear = morkAble_kDisabled; } + + mork_bool DoAutoClear() const + { return mEnv_AutoClear == morkAble_kEnabled; } + + void NewError(const char* inString); + void NewWarning(const char* inString); + + void ClearMorkErrorsAndWarnings(); // clear both errors & warnings + void AutoClearMorkErrorsAndWarnings(); // clear if auto is enabled + + void StubMethodOnlyError(); + void OutOfMemoryError(); + void NilPointerError(); + void NilPointerWarning(); + void CantMakeWhenBadError(); + void NewNonEnvError(); + void NilEnvSlotError(); + + void NonEnvTypeError(morkEnv* ev); + + // canonical env convenience methods to check for presence of errors: + mork_bool Good() const { return ( mEnv_ErrorCount == 0 ); } + mork_bool Bad() const { return ( mEnv_ErrorCount != 0 ); } + + nsIMdbEnv* AsMdbEnv() { return (nsIMdbEnv *) this; } + static morkEnv* FromMdbEnv(nsIMdbEnv* ioEnv); // dynamic type checking + + nsresult AsErr() const { return mEnv_ErrorCode; } + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakEnv(morkEnv* me, + morkEnv* ev, morkEnv** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongEnv(morkEnv* me, + morkEnv* ev, morkEnv** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +#undef MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING +#ifdef MOZ_IS_DESTRUCTIBLE +#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \ + static_assert(!MOZ_IS_DESTRUCTIBLE(X) || \ + mozilla::IsSame::value, \ + "Reference-counted class " #X " should not have a public destructor. " \ + "Try to make this class's destructor non-public. If that is really " \ + "not possible, you can whitelist this class by providing a " \ + "HasDangerousPublicDestructor specialization for it."); +#else +#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKENV_ */ diff --git a/db/mork/src/morkFactory.cpp b/db/mork/src/morkFactory.cpp new file mode 100644 index 0000000000..a3aeadfd4a --- /dev/null +++ b/db/mork/src/morkFactory.cpp @@ -0,0 +1,610 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +#include "morkFactory.h" +#endif + +#ifndef _ORKINHEAP_ +#include "orkinHeap.h" +#endif + +#ifndef _MORKFILE_ +#include "morkFile.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKTHUMB_ +#include "morkThumb.h" +#endif + +#ifndef _MORKWRITER_ +#include "morkWriter.h" +#endif +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkFactory::CloseMorkNode(morkEnv* ev) /*i*/ // CloseFactory() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseFactory(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkFactory::~morkFactory() /*i*/ // assert CloseFactory() executed earlier +{ + CloseFactory(&mFactory_Env); + MORK_ASSERT(mFactory_Env.IsShutNode()); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkFactory::morkFactory() // uses orkinHeap +: morkObject(morkUsage::kGlobal, (nsIMdbHeap*) 0, morkColor_kNone) +, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, + new orkinHeap()) +, mFactory_Heap() +{ + if ( mFactory_Env.Good() ) + { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +/*public non-poly*/ +morkFactory::morkFactory(nsIMdbHeap* ioHeap) +: morkObject(morkUsage::kHeap, ioHeap, morkColor_kNone) +, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap) +, mFactory_Heap() +{ + if ( mFactory_Env.Good() ) + { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +/*public non-poly*/ +morkFactory::morkFactory(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap) +, mFactory_Heap() +{ + if ( ev->Good() ) + { + mNode_Derived = morkDerived_kFactory; + mNode_Refs += morkFactory_kWeakRefCountBonus; + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkFactory, morkObject, nsIMdbFactory) + +extern "C" nsIMdbFactory* MakeMdbFactory() +{ + return new morkFactory(new orkinHeap()); +} + + +/*public non-poly*/ void +morkFactory::CloseFactory(morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mFactory_Env.CloseMorkNode(ev); + this->CloseObject(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +morkEnv* morkFactory::GetInternalFactoryEnv(nsresult* outErr) +{ + morkEnv* outEnv = 0; + if (IsNode() && IsOpenNode() && IsFactory() ) + { + morkEnv* fenv = &mFactory_Env; + if ( fenv && fenv->IsNode() && fenv->IsOpenNode() && fenv->IsEnv() ) + { + fenv->ClearMorkErrorsAndWarnings(); // drop any earlier errors + outEnv = fenv; + } + else + *outErr = morkEnv_kBadFactoryEnvError; + } + else + *outErr = morkEnv_kBadFactoryError; + + return outEnv; +} + + +void +morkFactory::NonFactoryTypeError(morkEnv* ev) +{ + ev->NewError("non morkFactory"); +} + + +NS_IMETHODIMP +morkFactory::OpenOldFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, + const char* inFilePath, + mork_bool inFrozen, nsIMdbFile** acqFile) + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + morkFile* file = nullptr; + if ( ev ) + { + if ( !ioHeap ) + ioHeap = &mFactory_Heap; + + file = morkFile::OpenOldFile(ev, ioHeap, inFilePath, inFrozen); + NS_IF_ADDREF( file ); + + outErr = ev->AsErr(); + } + if ( acqFile ) + *acqFile = file; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::CreateNewFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, + const char* inFilePath, nsIMdbFile** acqFile) + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + morkFile* file = nullptr; + if ( ev ) + { + if ( !ioHeap ) + ioHeap = &mFactory_Heap; + + file = morkFile::CreateNewFile(ev, ioHeap, inFilePath); + if ( file ) + NS_ADDREF(file); + + outErr = ev->AsErr(); + } + if ( acqFile ) + *acqFile = file; + + return outErr; +} +// } ----- end file methods ----- + +// { ----- begin env methods ----- +NS_IMETHODIMP +morkFactory::MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) +// ioHeap can be nil, causing a MakeHeap() style heap instance to be used +{ + nsresult outErr = NS_OK; + nsIMdbEnv* outEnv = 0; + mork_bool ownsHeap = (ioHeap == 0); + if ( !ioHeap ) + ioHeap = new orkinHeap(); + + if ( acqEnv && ioHeap ) + { + morkEnv* fenv = this->GetInternalFactoryEnv(&outErr); + if ( fenv ) + { + morkEnv* newEnv = new(*ioHeap, fenv) + morkEnv(morkUsage::kHeap, ioHeap, this, ioHeap); + + if ( newEnv ) + { + newEnv->mEnv_OwnsHeap = ownsHeap; + newEnv->mNode_Refs += morkEnv_kWeakRefCountEnvBonus; + NS_ADDREF(newEnv); + newEnv->mEnv_SelfAsMdbEnv = newEnv; + outEnv = newEnv; + } + else + outErr = morkEnv_kOutOfMemoryError; + } + + *acqEnv = outEnv; + } + else + outErr = morkEnv_kNilPointerError; + + return outErr; +} +// } ----- end env methods ----- + +// { ----- begin heap methods ----- +NS_IMETHODIMP +morkFactory::MakeHeap(nsIMdbEnv* mev, nsIMdbHeap** acqHeap) +{ + nsresult outErr = NS_OK; + nsIMdbHeap* outHeap = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + outHeap = new orkinHeap(); + if ( !outHeap ) + ev->OutOfMemoryError(); + } + MORK_ASSERT(acqHeap); + if ( acqHeap ) + *acqHeap = outHeap; + return outErr; +} +// } ----- end heap methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkFactory::MakeRow(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, + nsIMdbRow** acqRow) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// ioHeap can be nil, causing the heap associated with ev to be used +// } ----- end row methods ----- + +// { ----- begin port methods ----- +NS_IMETHODIMP +morkFactory::CanOpenFilePort( + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) +{ + nsresult outErr = NS_OK; + if ( outFormatVersion ) + { + outFormatVersion->mYarn_Fill = 0; + } + mdb_bool canOpenAsPort = morkBool_kFalse; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioFile && outCanOpen ) + { + canOpenAsPort = this->CanOpenMorkTextFile(ev, ioFile); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + + if ( outCanOpen ) + *outCanOpen = canOpenAsPort; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::OpenFilePort( + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) +{ + NS_ASSERTION(false, "this doesn't look implemented"); + MORK_USED_1(ioHeap); + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioFile && inOpenPolicy && acqThumb ) + { + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + +NS_IMETHODIMP +morkFactory::ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort() + nsIMdbEnv* mev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) +{ + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioThumb && acqPort ) + { + morkThumb* thumb = (morkThumb*) ioThumb; + morkStore* store = thumb->ThumbToOpenStore(ev); + if ( store ) + { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + NS_ADDREF(store); + outPort = store; + } + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqPort ) + *acqPort = outPort; + return outErr; +} +// } ----- end port methods ----- + +mork_bool +morkFactory::CanOpenMorkTextFile(morkEnv* ev, + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile) +{ + MORK_USED_1(ev); + mork_bool outBool = morkBool_kFalse; + mork_size headSize = MORK_STRLEN(morkWriter_kFileHeader); + + char localBuf[ 256 + 4 ]; // for extra for sloppy safety + mdbYarn localYarn; + mdbYarn* y = &localYarn; + y->mYarn_Buf = localBuf; // space to hold content + y->mYarn_Fill = 0; // no logical content yet + y->mYarn_Size = 256; // physical capacity is 256 bytes + y->mYarn_More = 0; + y->mYarn_Form = 0; + y->mYarn_Grow = 0; + + if ( ioFile ) + { + nsIMdbEnv* menv = ev->AsMdbEnv(); + mdb_size actualSize = 0; + ioFile->Get(menv, y->mYarn_Buf, y->mYarn_Size, /*pos*/ 0, &actualSize); + y->mYarn_Fill = actualSize; + + if ( y->mYarn_Buf && actualSize >= headSize && ev->Good() ) + { + mork_u1* buf = (mork_u1*) y->mYarn_Buf; + outBool = ( MORK_MEMCMP(morkWriter_kFileHeader, buf, headSize) == 0 ); + } + } + else + ev->NilPointerError(); + + return outBool; +} + +// { ----- begin store methods ----- +NS_IMETHODIMP +morkFactory::CanOpenFileStore( + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) +{ + mdb_bool canOpenAsStore = morkBool_kFalse; + mdb_bool canOpenAsPort = morkBool_kFalse; + if ( outFormatVersion ) + { + outFormatVersion->mYarn_Fill = 0; + } + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioFile && outCanOpenAsStore ) + { + // right now always say true; later we should look for magic patterns + canOpenAsStore = this->CanOpenMorkTextFile(ev, ioFile); + canOpenAsPort = canOpenAsStore; + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( outCanOpenAsStore ) + *outCanOpenAsStore = canOpenAsStore; + + if ( outCanOpenAsPort ) + *outCanOpenAsPort = canOpenAsPort; + + return outErr; +} + +NS_IMETHODIMP +morkFactory::OpenFileStore( // open an existing database + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( !ioHeap ) // need to use heap from env? + ioHeap = ev->mEnv_Heap; + + if ( ioFile && inOpenPolicy && acqThumb ) + { + morkStore* store = new(*ioHeap, ev) + morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap); + + if ( store ) + { + mork_bool frozen = morkBool_kFalse; // open store mutable access + if ( store->OpenStoreFile(ev, frozen, ioFile, inOpenPolicy) ) + { + morkThumb* thumb = morkThumb::Make_OpenFileStore(ev, ioHeap, store); + if ( thumb ) + { + outThumb = thumb; + thumb->AddRef(); + } + } +// store->CutStrongRef(mev); // always cut ref (handle has its own ref) + } + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + +NS_IMETHODIMP +morkFactory::ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* mev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) +{ + nsresult outErr = NS_OK; + nsIMdbStore* outStore = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioThumb && acqStore ) + { + morkThumb* thumb = (morkThumb*) ioThumb; + morkStore* store = thumb->ThumbToOpenStore(ev); + if ( store ) + { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + outStore = store; + NS_ADDREF(store); + } + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqStore ) + *acqStore = outStore; + return outErr; +} + +NS_IMETHODIMP +morkFactory::CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* mev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) +{ + nsresult outErr = NS_OK; + nsIMdbStore* outStore = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( !ioHeap ) // need to use heap from env? + ioHeap = ev->mEnv_Heap; + + if ( ioFile && inOpenPolicy && acqStore && ioHeap ) + { + morkStore* store = new(*ioHeap, ev) + morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap); + + if ( store ) + { + store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue; + store->mStore_CanDirty = morkBool_kTrue; + store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue); + + if ( store->CreateStoreFile(ev, ioFile, inOpenPolicy) ) + outStore = store; + NS_ADDREF(store); + } + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqStore ) + *acqStore = outStore; + return outErr; +} +// } ----- end store methods ----- + +// } ===== end nsIMdbFactory methods ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkFactory.h b/db/mork/src/morkFactory.h new file mode 100644 index 0000000000..544758f4cb --- /dev/null +++ b/db/mork/src/morkFactory.h @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKFACTORY_ +#define _MORKFACTORY_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _ORKINHEAP_ +#include "orkinHeap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbFactory; + +#define morkDerived_kFactory /*i*/ 0x4663 /* ascii 'Fc' */ +#define morkFactory_kWeakRefCountBonus 0 /* try NOT to leak all factories */ + +/*| morkFactory: +|*/ +class morkFactory : public morkObject, public nsIMdbFactory { // nsIMdbObject + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // state is public because the entire Mork system is private + + morkEnv mFactory_Env; // private env instance used internally + orkinHeap mFactory_Heap; + + NS_DECL_ISUPPORTS_INHERITED +// { ===== begin morkNode interface ===== +public: // morkFactory virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseFactory() only if open + + +// { ===== begin nsIMdbFactory methods ===== + + // { ----- begin file methods ----- + NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, + mdb_bool inFrozen, nsIMdbFile** acqFile) override; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, + nsIMdbFile** acqFile) override; + // Choose some subclass of nsIMdbFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + // } ----- end file methods ----- + + // { ----- begin env methods ----- + NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) override; // new env + // ioHeap can be nil, causing a MakeHeap() style heap instance to be used + // } ----- end env methods ----- + + // { ----- begin heap methods ----- + NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) override; // new heap + // } ----- end heap methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) override; // new row + // ioHeap can be nil, causing the heap associated with ev to be used + // } ----- end row methods ----- + + // { ----- begin port methods ----- + NS_IMETHOD CanOpenFilePort( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpen, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) override; // informal file format description + + NS_IMETHOD OpenFilePort( + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for readonly import + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental port open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance. + + NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status + nsIMdbPort** acqPort) override; // acquire new port object + // } ----- end port methods ----- + + // { ----- begin store methods ----- + NS_IMETHOD CanOpenFileStore( + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to investigate + // const mdbYarn* inFirst512Bytes, + nsIMdbFile* ioFile, // db abstract file interface + mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed + mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed + mdbYarn* outFormatVersion) override; // informal file format description + + NS_IMETHOD OpenFileStore( // open an existing database + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // the file to open for general db usage + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental store open + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance. + + NS_IMETHOD + ThumbToOpenStore( // redeem completed thumb from OpenFileStore() + nsIMdbEnv* ev, // context + nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status + nsIMdbStore** acqStore) override; // acquire new db store object + + NS_IMETHOD CreateNewFileStore( // create a new db with minimal content + nsIMdbEnv* ev, // context + nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used + // const char* inFilePath, // name of file which should not yet exist + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db + nsIMdbStore** acqStore) override; // acquire new db store object + // } ----- end store methods ----- + +// } ===== end nsIMdbFactory methods ===== + +public: // morkYarn construction & destruction + morkFactory(); // uses orkinHeap + morkFactory(nsIMdbHeap* ioHeap); // caller supplied heap + morkFactory(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseFactory(morkEnv* ev); // called by CloseMorkNode(); + + +public: // morkNode memory management operators + void* operator new(size_t inSize) CPP_THROW_NEW + { return ::operator new(inSize); } + + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkNode::MakeNew(inSize, ioHeap, ev); } + +private: // copying is not allowed + morkFactory(const morkFactory& other); + morkFactory& operator=(const morkFactory& other); + virtual ~morkFactory(); // assert that CloseFactory() executed earlier + +public: // dynamic type identification + mork_bool IsFactory() const + { return IsNode() && mNode_Derived == morkDerived_kFactory; } +// } ===== end morkNode methods ===== + +public: // other factory methods + + void NonFactoryTypeError(morkEnv* ev); + morkEnv* GetInternalFactoryEnv(nsresult* outErr); + mork_bool CanOpenMorkTextFile(morkEnv* ev, nsIMdbFile* ioFile); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakFactory(morkFactory* me, + morkEnv* ev, morkFactory** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongFactory(morkFactory* me, + morkEnv* ev, morkFactory** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKFACTORY_ */ diff --git a/db/mork/src/morkFile.cpp b/db/mork/src/morkFile.cpp new file mode 100644 index 0000000000..040d1a8dcb --- /dev/null +++ b/db/mork/src/morkFile.cpp @@ -0,0 +1,874 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKFILE_ +#include "morkFile.h" +#endif + +#ifdef MORK_WIN +#include "io.h" +#include +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkFile::CloseMorkNode(morkEnv* ev) // CloseFile() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseFile(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkFile::~morkFile() // assert CloseFile() executed earlier +{ + MORK_ASSERT(mFile_Frozen==0); + MORK_ASSERT(mFile_DoTrace==0); + MORK_ASSERT(mFile_IoOpen==0); + MORK_ASSERT(mFile_Active==0); +} + +/*public non-poly*/ +morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mFile_Frozen( 0 ) +, mFile_DoTrace( 0 ) +, mFile_IoOpen( 0 ) +, mFile_Active( 0 ) + +, mFile_SlotHeap( 0 ) +, mFile_Name( 0 ) +, mFile_Thief( 0 ) +{ + if ( ev->Good() ) + { + if ( ioSlotHeap ) + { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap); + if ( ev->Good() ) + mNode_Derived = morkDerived_kFile; + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkFile, morkObject, nsIMdbFile) +/*public non-poly*/ void +morkFile::CloseFile(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mFile_Frozen = 0; + mFile_DoTrace = 0; + mFile_IoOpen = 0; + mFile_Active = 0; + + if ( mFile_Name ) + this->SetFileName(ev, (const char*) 0); + + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mFile_SlotHeap); + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mFile_Thief); + + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ morkFile* +morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, mork_bool inFrozen) + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. +{ + return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen); +} + +/*static*/ morkFile* +morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath) + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. +{ + return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath); +} + +void +morkFile::NewMissingIoError(morkEnv* ev) const +{ + ev->NewError("file missing io"); +} + +/*static*/ void +morkFile::NonFileTypeError(morkEnv* ev) +{ + ev->NewError("non morkFile"); +} + +/*static*/ void +morkFile::NilSlotHeapError(morkEnv* ev) +{ + ev->NewError("nil mFile_SlotHeap"); +} + +/*static*/ void +morkFile::NilFileNameError(morkEnv* ev) +{ + ev->NewError("nil mFile_Name"); +} + +void +morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief) +{ + nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief); +} + +void +morkFile::SetFileName(morkEnv* ev, const char* inName) // inName can be nil +{ + nsIMdbHeap* heap = mFile_SlotHeap; + if ( heap ) + { + char* name = mFile_Name; + if ( name ) + { + mFile_Name = 0; + ev->FreeString(heap, name); + } + if ( ev->Good() && inName ) + mFile_Name = ev->CopyString(heap, inName); + } + else + this->NilSlotHeapError(ev); +} + +void +morkFile::NewFileDownError(morkEnv* ev) const +// call NewFileDownError() when either IsOpenAndActiveFile() +// is false, or when IsOpenActiveAndMutableFile() is false. +{ + if ( this->IsOpenNode() ) + { + if ( this->FileActive() ) + { + if ( this->FileFrozen() ) + { + ev->NewError("file frozen"); + } + else + ev->NewError("unknown file problem"); + } + else + ev->NewError("file not active"); + } + else + ev->NewError("file not open"); +} + +void +morkFile::NewFileErrnoError(morkEnv* ev) const +// call NewFileErrnoError() to convert std C errno into AB fault +{ + const char* errnoString = strerror(errno); + ev->NewError(errnoString); // maybe pass value of strerror() instead +} + +// ````` ````` ````` ````` newlines ````` ````` ````` ````` + +#if defined(MORK_MAC) + static const char morkFile_kNewlines[] = + "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015"; +# define morkFile_kNewlinesCount 16 +#else +# if defined(MORK_WIN) + static const char morkFile_kNewlines[] = + "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012"; +# define morkFile_kNewlinesCount 8 +# else +# ifdef MORK_UNIX + static const char morkFile_kNewlines[] = + "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012"; +# define morkFile_kNewlinesCount 16 +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ + +mork_size +morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines) + // WriteNewlines() returns the number of bytes written. +{ + mork_size outSize = 0; + while ( inNewlines && ev->Good() ) // more newlines to write? + { + mork_u4 quantum = inNewlines; + if ( quantum > morkFile_kNewlinesCount ) + quantum = morkFile_kNewlinesCount; + + mork_size quantumSize = quantum * mork_kNewlineSize; + mdb_size bytesWritten; + this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten); + outSize += quantumSize; + inNewlines -= quantum; + } + return outSize; +} + +NS_IMETHODIMP +morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos) +{ + nsresult outErr = NS_OK; + mdb_pos pos = -1; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + pos = Length(ev); + outErr = ev->AsErr(); + if ( outPos ) + *outPos = pos; + return outErr; +} + +NS_IMETHODIMP +morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) +{ + nsresult rv = NS_OK; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mdb_pos outPos; + Seek(mev, inPos, &outPos); + if ( ev->Good() ) + rv = Read(mev, outBuf, inSize, outActualSize); + } + return rv; +} + +NS_IMETHODIMP +morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) +{ + nsresult outErr = NS_OK; + *outActualSize = 0; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mdb_pos outPos; + + Seek(mev, inPos, &outPos); + if ( ev->Good() ) + Write(mev, inBuf, inSize, outActualSize); + outErr = ev->AsErr(); + } + return outErr; +} + +// { ----- begin path methods ----- +NS_IMETHODIMP +morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath) +{ + nsresult outErr = NS_OK; + if ( outFilePath ) + outFilePath->mYarn_Fill = 0; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + ev->StringToYarn(GetFileNameString(), outFilePath); + outErr = ev->AsErr(); + } + return outErr; +} + +// } ----- end path methods ----- + +// { ----- begin replacement methods ----- + + +NS_IMETHODIMP +morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief) +{ + nsresult outErr = NS_OK; + nsIMdbFile* outThief = 0; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + outThief = GetThief(); + NS_IF_ADDREF(outThief); + outErr = ev->AsErr(); + } + if ( acqThief ) + *acqThief = outThief; + return outErr; +} + +// } ----- end replacement methods ----- + +// { ----- begin versioning methods ----- + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkStdioFile::CloseMorkNode(morkEnv* ev) // CloseStdioFile() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseStdioFile(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkStdioFile::~morkStdioFile() // assert CloseStdioFile() executed earlier +{ + if (mStdioFile_File) + CloseStdioFile(mMorkEnv); + MORK_ASSERT(mStdioFile_File==0); +} + +/*public non-poly*/ void +morkStdioFile::CloseStdioFile(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() ) + { + this->CloseStdio(ev); + } + + mStdioFile_File = 0; + + this->CloseFile(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// compatible with the morkFile::MakeFile() entry point + +/*static*/ morkStdioFile* +morkStdioFile::OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, mork_bool inFrozen) +{ + morkStdioFile* outFile = 0; + if ( ioHeap && inFilePath ) + { + const char* mode = (inFrozen)? "rb" : "rb+"; + outFile = new(*ioHeap, ev) + morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); + + if ( outFile ) + { + outFile->SetFileFrozen(inFrozen); + } + } + else + ev->NilPointerError(); + + return outFile; +} + +/*static*/ morkStdioFile* +morkStdioFile::CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath) +{ + morkStdioFile* outFile = 0; + if ( ioHeap && inFilePath ) + { + const char* mode = "wb+"; + outFile = new(*ioHeap, ev) + morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); + } + else + ev->NilPointerError(); + + return outFile; +} + + + +NS_IMETHODIMP +morkStdioFile::BecomeTrunk(nsIMdbEnv* ev) + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. +{ + return Flush(ev); +} + +NS_IMETHODIMP +morkStdioFile::AcquireBud(nsIMdbEnv * mdbev, nsIMdbHeap* ioHeap, nsIMdbFile **acquiredFile) + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. +{ + NS_ENSURE_ARG(acquiredFile); + MORK_USED_1(ioHeap); + nsresult rv = NS_OK; + morkFile* outFile = 0; + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + + if ( this->IsOpenAndActiveFile() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { +//#ifdef MORK_WIN +// truncate(file, /*eof*/ 0); +//#else /*MORK_WIN*/ + char* name = mFile_Name; + if ( name ) + { + if ( MORK_FILECLOSE(file) >= 0 ) + { + this->SetFileActive(morkBool_kFalse); + this->SetFileIoOpen(morkBool_kFalse); + mStdioFile_File = 0; + + file = MORK_FILEOPEN(name, "wb+"); // open for write, discarding old content + if ( file ) + { + mStdioFile_File = file; + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + this->SetFileFrozen(morkBool_kFalse); + } + else + this->new_stdio_file_fault(ev); + } + else + this->new_stdio_file_fault(ev); + } + else + this->NilFileNameError(ev); + +//#endif /*MORK_WIN*/ + + if ( ev->Good() && this->AddStrongRef(ev->AsMdbEnv()) ) + { + outFile = this; + AddRef(); + } + } + else if ( mFile_Thief ) + { + rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile); + } + else + this->NewMissingIoError(ev); + } + else this->NewFileDownError(ev); + + *acquiredFile = outFile; + return rv; +} + +mork_pos +morkStdioFile::Length(morkEnv * ev) const +{ + mork_pos outPos = 0; + + if ( this->IsOpenAndActiveFile() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + long start = MORK_FILETELL(file); + if ( start >= 0 ) + { + long fore = MORK_FILESEEK(file, 0, SEEK_END); + if ( fore >= 0 ) + { + long eof = MORK_FILETELL(file); + if ( eof >= 0 ) + { + long back = MORK_FILESEEK(file, start, SEEK_SET); + if ( back >= 0 ) + outPos = eof; + else + this->new_stdio_file_fault(ev); + } + else this->new_stdio_file_fault(ev); + } + else this->new_stdio_file_fault(ev); + } + else this->new_stdio_file_fault(ev); + } + else if ( mFile_Thief ) + mFile_Thief->Eof(ev->AsMdbEnv(), &outPos); + else + this->NewMissingIoError(ev); + } + else this->NewFileDownError(ev); + + return outPos; +} + +NS_IMETHODIMP +morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos *outPos) const +{ + nsresult rv = NS_OK; + NS_ENSURE_ARG(outPos); + morkEnv* mev = morkEnv::FromMdbEnv(ev); + if ( this->IsOpenAndActiveFile() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + long where = MORK_FILETELL(file); + if ( where >= 0 ) + *outPos = where; + else + this->new_stdio_file_fault(mev); + } + else if ( mFile_Thief ) + mFile_Thief->Tell(ev, outPos); + else + this->NewMissingIoError(mev); + } + else this->NewFileDownError(mev); + + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_num *outCount) +{ + nsresult rv = NS_OK; + morkEnv* mev = morkEnv::FromMdbEnv(ev); + if ( this->IsOpenAndActiveFile() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + long count = (long) MORK_FILEREAD(outBuf, inSize, file); + if ( count >= 0 ) + { + *outCount = (mork_num) count; + } + else this->new_stdio_file_fault(mev); + } + else if ( mFile_Thief ) + mFile_Thief->Read(ev, outBuf, inSize, outCount); + else + this->NewMissingIoError(mev); + } + else this->NewFileDownError(mev); + + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos *aOutPos) +{ + mork_pos outPos = 0; + nsresult rv = NS_OK; + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + + if ( this->IsOpenOrClosingNode() && this->FileActive() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + long where = MORK_FILESEEK(file, inPos, SEEK_SET); + if ( where >= 0 ) + outPos = inPos; + else + this->new_stdio_file_fault(ev); + } + else if ( mFile_Thief ) + mFile_Thief->Seek(mdbev, inPos, aOutPos); + else + this->NewMissingIoError(ev); + } + else this->NewFileDownError(ev); + + *aOutPos = outPos; + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize, mork_size *aOutSize) +{ + mork_num outCount = 0; + nsresult rv = NS_OK; + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + if ( this->IsOpenActiveAndMutableFile() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + fwrite(inBuf, 1, inSize, file); + if ( !ferror(file) ) + outCount = inSize; + else + this->new_stdio_file_fault(ev); + } + else if ( mFile_Thief ) + mFile_Thief->Write(mdbev, inBuf, inSize, &outCount); + else + this->NewMissingIoError(ev); + } + else this->NewFileDownError(ev); + + *aOutSize = outCount; + return rv; +} + +NS_IMETHODIMP +morkStdioFile::Flush(nsIMdbEnv* mdbev) +{ + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + if ( this->IsOpenOrClosingNode() && this->FileActive() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( file ) + { + MORK_FILEFLUSH(file); + + } + else if ( mFile_Thief ) + mFile_Thief->Flush(mdbev); + else + this->NewMissingIoError(ev); + } + else this->NewFileDownError(ev); + return NS_OK; +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +//protected: // protected non-poly morkStdioFile methods + +void +morkStdioFile::new_stdio_file_fault(morkEnv* ev) const +{ + FILE* file = (FILE*) mStdioFile_File; + + int copyErrno = errno; // facilitate seeing error in debugger + + // bunch of stuff not ported here + if ( !copyErrno && file ) + { + copyErrno = ferror(file); + errno = copyErrno; + } + + this->NewFileErrnoError(ev); +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +//public: // public non-poly morkStdioFile methods + + +/*public non-poly*/ +morkStdioFile::morkStdioFile(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkFile(ev, inUsage, ioHeap, ioSlotHeap) +, mStdioFile_File( 0 ) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kStdioFile; +} + +morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + const char* inName, const char* inMode) + // calls OpenStdio() after construction +: morkFile(ev, inUsage, ioHeap, ioSlotHeap) +, mStdioFile_File( 0 ) +{ + if ( ev->Good() ) + this->OpenStdio(ev, inName, inMode); +} + +morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + void* ioFile, const char* inName, mork_bool inFrozen) + // calls UseStdio() after construction +: morkFile(ev, inUsage, ioHeap, ioSlotHeap) +, mStdioFile_File( 0 ) +{ + if ( ev->Good() ) + this->UseStdio(ev, ioFile, inName, inFrozen); +} + +void +morkStdioFile::OpenStdio(morkEnv* ev, const char* inName, const char* inMode) + // Open a new FILE with name inName, using mode flags from inMode. +{ + if ( ev->Good() ) + { + if ( !inMode ) + inMode = ""; + + mork_bool frozen = (*inMode == 'r'); // cursory attempt to note readonly + + if ( this->IsOpenNode() ) + { + if ( !this->FileActive() ) + { + this->SetFileIoOpen(morkBool_kFalse); + if ( inName && *inName ) + { + this->SetFileName(ev, inName); + if ( ev->Good() ) + { + FILE* file = MORK_FILEOPEN(inName, inMode); + if ( file ) + { + mStdioFile_File = file; + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + this->SetFileFrozen(frozen); + } + else + this->new_stdio_file_fault(ev); + } + } + else ev->NewError("no file name"); + } + else ev->NewError("file already active"); + } + else this->NewFileDownError(ev); + } +} + +void +morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const char* inName, + mork_bool inFrozen) + // Use an existing file, like stdin/stdout/stderr, which should not + // have the io stream closed when the file is closed. The ioFile + // parameter must actually be of type FILE (but we don't want to make + // this header file include the stdio.h header file). +{ + if ( ev->Good() ) + { + if ( this->IsOpenNode() ) + { + if ( !this->FileActive() ) + { + if ( ioFile ) + { + this->SetFileIoOpen(morkBool_kFalse); + this->SetFileName(ev, inName); + if ( ev->Good() ) + { + mStdioFile_File = ioFile; + this->SetFileActive(morkBool_kTrue); + this->SetFileFrozen(inFrozen); + } + } + else + ev->NilPointerError(); + } + else ev->NewError("file already active"); + } + else this->NewFileDownError(ev); + } +} + +void +morkStdioFile::CloseStdio(morkEnv* ev) + // Close the stream io if both and FileActive() and FileIoOpen(), but + // this does not close this instances (like CloseStdioFile() does). + // If stream io was made active by means of calling UseStdio(), + // then this method does little beyond marking the stream inactive + // because FileIoOpen() is false. +{ + if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( MORK_FILECLOSE(file) < 0 ) + this->new_stdio_file_fault(ev); + + mStdioFile_File = 0; + this->SetFileActive(morkBool_kFalse); + this->SetFileIoOpen(morkBool_kFalse); + } +} + + +NS_IMETHODIMP +morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. +{ + morkEnv *mev = morkEnv::FromMdbEnv(ev); + if ( mStdioFile_File && FileActive() && FileIoOpen() ) + { + FILE* file = (FILE*) mStdioFile_File; + if ( MORK_FILECLOSE(file) < 0 ) + new_stdio_file_fault(mev); + + mStdioFile_File = 0; + } + SetThief(mev, ioThief); + return NS_OK; +} + + +#if defined(MORK_WIN) + +void mork_fileflush(FILE * file) +{ + fflush(file); +} + +#endif /*MORK_WIN*/ + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkFile.h b/db/mork/src/morkFile.h new file mode 100644 index 0000000000..39242ce735 --- /dev/null +++ b/db/mork/src/morkFile.h @@ -0,0 +1,355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKFILE_ +#define _MORKFILE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkFile: abstract file interface + */ + +#define morkDerived_kFile /*i*/ 0x4669 /* ascii 'Fi' */ + +class morkFile /*d*/ : public morkObject, public nsIMdbFile { /* ````` simple file API ````` */ + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +// public: // slots inherited from morkObject (meant to inform only) + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected morkFile members (similar to public domain IronDoc) + virtual ~morkFile(); // assert that CloseFile() executed earlier + + mork_u1 mFile_Frozen; // 'F' => file allows only read access + mork_u1 mFile_DoTrace; // 'T' trace if ev->DoTrace() + mork_u1 mFile_IoOpen; // 'O' => io stream is open (& needs a close) + mork_u1 mFile_Active; // 'A' => file is active and usable + + nsIMdbHeap* mFile_SlotHeap; // heap for Name and other allocated slots + char* mFile_Name; // can be nil if SetFileName() is never called + // mFile_Name convention: managed with morkEnv::CopyString()/FreeString() + + nsIMdbFile* mFile_Thief; // from a call to orkinFile::Steal() + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + NS_DECL_ISUPPORTS_INHERITED + virtual void CloseMorkNode(morkEnv* ev) override; // CloseFile() only if open + +public: // morkFile construction & destruction + morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void CloseFile(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkFile(const morkFile& other); + morkFile& operator=(const morkFile& other); + +public: // dynamic type identification + mork_bool IsFile() const + { return IsNode() && mNode_Derived == morkDerived_kFile; } +// } ===== end morkNode methods ===== + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public static standard file creation entry point + + static morkFile* OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, mork_bool inFrozen); + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be open and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + + static morkFile* CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath); + // Choose some subclass of morkFile to instantiate, in order to read + // (and write if not frozen) the file known by inFilePath. The file + // returned should be created and ready for use, and presumably positioned + // at the first byte position of the file. The exact manner in which + // files must be opened is considered a subclass specific detail, and + // other portions or Mork source code don't want to know how it's done. + +public: // non-poly morkFile methods + + mork_bool FileFrozen() const { return mFile_Frozen == 'F'; } + mork_bool FileDoTrace() const { return mFile_DoTrace == 'T'; } + mork_bool FileIoOpen() const { return mFile_IoOpen == 'O'; } + mork_bool FileActive() const { return mFile_Active == 'A'; } + + void SetFileFrozen(mork_bool b) { mFile_Frozen = (mork_u1) ((b)? 'F' : 0); } + void SetFileDoTrace(mork_bool b) { mFile_DoTrace = (mork_u1) ((b)? 'T' : 0); } + void SetFileIoOpen(mork_bool b) { mFile_IoOpen = (mork_u1) ((b)? 'O' : 0); } + void SetFileActive(mork_bool b) { mFile_Active = (mork_u1) ((b)? 'A' : 0); } + + + mork_bool IsOpenActiveAndMutableFile() const + { return ( IsOpenNode() && FileActive() && !FileFrozen() ); } + // call IsOpenActiveAndMutableFile() before writing a file + + mork_bool IsOpenAndActiveFile() const + { return ( this->IsOpenNode() && this->FileActive() ); } + // call IsOpenAndActiveFile() before using a file + + + nsIMdbFile* GetThief() const { return mFile_Thief; } + void SetThief(morkEnv* ev, nsIMdbFile* ioThief); // ioThief can be nil + + const char* GetFileNameString() const { return mFile_Name; } + void SetFileName(morkEnv* ev, const char* inName); // inName can be nil + static void NilSlotHeapError(morkEnv* ev); + static void NilFileNameError(morkEnv* ev); + static void NonFileTypeError(morkEnv* ev); + + void NewMissingIoError(morkEnv* ev) const; + + void NewFileDownError(morkEnv* ev) const; + // call NewFileDownError() when either IsOpenAndActiveFile() + // is false, or when IsOpenActiveAndMutableFile() is false. + + void NewFileErrnoError(morkEnv* ev) const; + // call NewFileErrnoError() to convert std C errno into AB fault + + mork_size WriteNewlines(morkEnv* ev, mork_count inNewlines); + // WriteNewlines() returns the number of bytes written. + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakFile(morkFile* me, + morkEnv* ev, morkFile** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongFile(morkFile* me, + morkEnv* ev, morkFile** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +public: + virtual mork_pos Length(morkEnv* ev) const = 0; // eof + // nsIMdbFile methods + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override = 0; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) override = 0; + NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) override; + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) override = 0; + NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) override; + // } ----- end read methods ----- + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) override = 0; + NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_pos inPos, mdb_size* outActualSize) override; + NS_IMETHOD Flush(nsIMdbEnv* ev) override = 0; + // } ----- end attribute methods ----- + + // { ----- begin path methods ----- + NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) override ; + // } ----- end path methods ----- + + // { ----- begin replacement methods ----- + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override = 0; + NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) override; + // } ----- end replacement methods ----- + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override = 0; + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) override = 0; + // } ----- end versioning methods ----- + +// } ===== end nsIMdbFile methods ===== + +}; + +/*============================================================================= + * morkStdioFile: concrete file using standard C file io + */ + +#define morkDerived_kStdioFile /*i*/ 0x7346 /* ascii 'sF' */ + +class morkStdioFile /*d*/ : public morkFile { /* `` copied from IronDoc `` */ + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected morkStdioFile members + + void* mStdioFile_File; + // actually type FILE*, but using opaque void* type + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseStdioFile() only if open + virtual ~morkStdioFile(); // assert that CloseStdioFile() executed earlier + +public: // morkStdioFile construction & destruction + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseStdioFile(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkStdioFile(const morkStdioFile& other); + morkStdioFile& operator=(const morkStdioFile& other); + +public: // dynamic type identification + mork_bool IsStdioFile() const + { return IsNode() && mNode_Derived == morkDerived_kStdioFile; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonStdioFileTypeError(morkEnv* ev); + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // compatible with the morkFile::OpenOldFile() entry point + + static morkStdioFile* OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath, mork_bool inFrozen); + + static morkStdioFile* CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap, + const char* inFilePath); + + virtual mork_pos Length(morkEnv* ev) const override; // eof + + NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override; + NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) override; +// NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos); + // } ----- end pos methods ----- + + // { ----- begin read methods ----- + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize, + mdb_size* outActualSize) override; + + // { ----- begin write methods ----- + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, + mdb_size* outActualSize) override; +// NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize, +// mdb_pos inPos, mdb_size* outActualSize); + NS_IMETHOD Flush(nsIMdbEnv* ev) override; + // } ----- end attribute methods ----- + + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override; + + // { ----- begin versioning methods ----- + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override; + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, + nsIMdbFile** acqBud) override; + // } ----- end versioning methods ----- + +// } ===== end nsIMdbFile methods ===== + +// ````` ````` ````` ````` ````` ````` ````` ````` + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected non-poly morkStdioFile methods + + void new_stdio_file_fault(morkEnv* ev) const; + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkStdioFile methods + + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + const char* inName, const char* inMode); + // calls OpenStdio() after construction + + morkStdioFile(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, + void* ioFile, const char* inName, mork_bool inFrozen); + // calls UseStdio() after construction + + void OpenStdio(morkEnv* ev, const char* inName, const char* inMode); + // Open a new FILE with name inName, using mode flags from inMode. + + void UseStdio(morkEnv* ev, void* ioFile, const char* inName, + mork_bool inFrozen); + // Use an existing file, like stdin/stdout/stderr, which should not + // have the io stream closed when the file is closed. The ioFile + // parameter must actually be of type FILE (but we don't want to make + // this header file include the stdio.h header file). + + void CloseStdio(morkEnv* ev); + // Close the stream io if both and FileActive() and FileIoOpen(), but + // this does not close this instances (like CloseStdioFile() does). + // If stream io was made active by means of calling UseStdio(), + // then this method does little beyond marking the stream inactive + // because FileIoOpen() is false. + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakStdioFile(morkStdioFile* me, + morkEnv* ev, morkStdioFile** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongStdioFile(morkStdioFile* me, + morkEnv* ev, morkStdioFile** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKFILE_ */ diff --git a/db/mork/src/morkHandle.cpp b/db/mork/src/morkHandle.cpp new file mode 100644 index 0000000000..f6d426887d --- /dev/null +++ b/db/mork/src/morkHandle.cpp @@ -0,0 +1,423 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKFACTORY_ +#include "morkFactory.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKHANDLE_ +#include "morkHandle.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkHandle::CloseMorkNode(morkEnv* ev) // CloseHandle() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseHandle(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkHandle::~morkHandle() // assert CloseHandle() executed earlier +{ + MORK_ASSERT(mHandle_Env==0); + MORK_ASSERT(mHandle_Face==0); + MORK_ASSERT(mHandle_Object==0); + MORK_ASSERT(mHandle_Magic==0); + MORK_ASSERT(mHandle_Tag==morkHandle_kTag); // should still have correct tag +} + +/*public non-poly*/ +morkHandle::morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool + morkHandleFace* ioFace, // must not be nil, cookie for this handle + morkObject* ioObject, // must not be nil, the object for this handle + mork_magic inMagic) // magic sig to denote specific subclass +: morkNode(ev, morkUsage::kPool, (nsIMdbHeap*) 0L) +, mHandle_Tag( 0 ) +, mHandle_Env( ev ) +, mHandle_Face( ioFace ) +, mHandle_Object( 0 ) +, mHandle_Magic( 0 ) +{ + if ( ioFace && ioObject ) + { + if ( ev->Good() ) + { + mHandle_Tag = morkHandle_kTag; + morkObject::SlotStrongObject(ioObject, ev, &mHandle_Object); + morkHandle::SlotWeakHandle(this, ev, &ioObject->mObject_Handle); + if ( ev->Good() ) + { + mHandle_Magic = inMagic; + mNode_Derived = morkDerived_kHandle; + } + } + else + ev->CantMakeWhenBadError(); + } + else + ev->NilPointerError(); +} + +/*public non-poly*/ void +morkHandle::CloseHandle(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkObject* obj = mHandle_Object; + mork_bool objDidRefSelf = ( obj && obj->mObject_Handle == this ); + if ( objDidRefSelf ) + obj->mObject_Handle = 0; // drop the reference + + morkObject::SlotStrongObject((morkObject*) 0, ev, &mHandle_Object); + mHandle_Magic = 0; + // note mHandle_Tag MUST stay morkHandle_kTag for morkNode::ZapOld() + this->MarkShut(); + + if ( objDidRefSelf ) + this->CutWeakRef(ev); // do last, because it might self destroy + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +void morkHandle::NilFactoryError(morkEnv* ev) const +{ + ev->NewError("nil mHandle_Factory"); +} + +void morkHandle::NilHandleObjectError(morkEnv* ev) const +{ + ev->NewError("nil mHandle_Object"); +} + +void morkHandle::NonNodeObjectError(morkEnv* ev) const +{ + ev->NewError("non-node mHandle_Object"); +} + +void morkHandle::NonOpenObjectError(morkEnv* ev) const +{ + ev->NewError("non-open mHandle_Object"); +} + +void morkHandle::NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const +{ + MORK_USED_1(inMagic); + ev->NewError("wrong mHandle_Magic"); +} + +void morkHandle::NewDownHandleError(morkEnv* ev) const +{ + if ( this->IsHandle() ) + { + if ( this->GoodHandleTag() ) + { + if ( this->IsOpenNode() ) + ev->NewError("unknown down morkHandle error"); + else + this->NonOpenNodeError(ev); + } + else + ev->NewError("wrong morkHandle tag"); + } + else + ev->NewError("non morkHandle"); +} + +morkObject* morkHandle::GetGoodHandleObject(morkEnv* ev, + mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const +{ + morkObject* outObject = 0; + if ( this->IsHandle() && this->GoodHandleTag() && + ( inClosedOkay || this->IsOpenNode() ) ) + { + if ( !inMagicType || mHandle_Magic == inMagicType ) + { + morkObject* obj = this->mHandle_Object; + if ( obj ) + { + if ( obj->IsNode() ) + { + if ( inClosedOkay || obj->IsOpenNode() ) + { + if ( this->IsMutable() || !inMutable ) + outObject = obj; + else + this->NonMutableNodeError(ev); + } + else + this->NonOpenObjectError(ev); + } + else + this->NonNodeObjectError(ev); + } + else if ( !inClosedOkay ) + this->NilHandleObjectError(ev); + } + else + this->NewBadMagicHandleError(ev, inMagicType); + } + else + this->NewDownHandleError(ev); + + MORK_ASSERT(outObject || inClosedOkay); + return outObject; +} + + +morkEnv* +morkHandle::CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable, + mork_bool inClosedOkay, nsresult* outErr) const +{ + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkObject* obj = this->GetGoodHandleObject(ev, inMutable, + /*magic*/ 0, inClosedOkay); + if ( obj ) + { + outEnv = ev; + } + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv || inClosedOkay); + return outEnv; +} + +// { ===== begin nsIMdbObject methods ===== + +// { ----- begin attribute methods ----- +/*virtual*/ nsresult +morkHandle::Handle_IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly) +{ + nsresult outErr = NS_OK; + mdb_bool readOnly = mdbBool_kTrue; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + readOnly = mHandle_Object->IsFrozen(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outIsReadonly); + if ( outIsReadonly ) + *outIsReadonly = readOnly; + + return outErr; +} +// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. +// } ----- end attribute methods ----- + +// { ----- begin factory methods ----- +/*virtual*/ nsresult +morkHandle::Handle_GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory) +{ + nsresult outErr = NS_OK; + nsIMdbFactory* handle = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + morkFactory* factory = ev->mEnv_Factory; + if ( factory ) + { + handle = factory; + NS_ADDREF(handle); + } + else + this->NilFactoryError(ev); + + outErr = ev->AsErr(); + } + + MORK_ASSERT(acqFactory); + if ( acqFactory ) + *acqFactory = handle; + + return outErr; +} +// } ----- end factory methods ----- + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +/*virtual*/ nsresult +morkHandle::Handle_GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) +{ + nsresult outErr = NS_OK; + mdb_count count = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + count = this->WeakRefsOnly(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outCount); + if ( outCount ) + *outCount = count; + + return outErr; +} +/*virtual*/ nsresult +morkHandle::Handle_GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) +{ + nsresult outErr = NS_OK; + mdb_count count = 0; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + count = this->StrongRefsOnly(); + + outErr = ev->AsErr(); + } + MORK_ASSERT(outCount); + if ( outCount ) + *outCount = count; + + return outErr; +} + +/*virtual*/ nsresult +morkHandle::Handle_AddWeakRef(nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + this->AddWeakRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} +/*virtual*/ nsresult +morkHandle::Handle_AddStrongRef(nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kFalse, &outErr); + if ( ev ) + { + this->AddStrongRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} + +/*virtual*/ nsresult +morkHandle::Handle_CutWeakRef(nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + this->CutWeakRef(ev); + outErr = ev->AsErr(); + } + + return outErr; +} +/*virtual*/ nsresult +morkHandle::Handle_CutStrongRef(nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + this->CutStrongRef(ev); + outErr = ev->AsErr(); + } + return outErr; +} + +/*virtual*/ nsresult +morkHandle::Handle_CloseMdbObject(nsIMdbEnv* mev) +// called at strong refs zero +{ + // if only one ref, Handle_CutStrongRef will clean up better. + if (mNode_Uses == 1) + return Handle_CutStrongRef(mev); + + nsresult outErr = NS_OK; + + if ( this->IsNode() && this->IsOpenNode() ) + { + morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse, + /*inClosedOkay*/ morkBool_kTrue, &outErr); + if ( ev ) + { + morkObject* object = mHandle_Object; + if ( object && object->IsNode() && object->IsOpenNode() ) + object->CloseMorkNode(ev); + + this->CloseMorkNode(ev); + outErr = ev->AsErr(); + } + } + return outErr; +} + +/*virtual*/ nsresult +morkHandle::Handle_IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) +{ + MORK_USED_1(mev); + nsresult outErr = NS_OK; + + MORK_ASSERT(outOpen); + if ( outOpen ) + *outOpen = this->IsOpenNode(); + + return outErr; +} +// } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkHandle.h b/db/mork/src/morkHandle.h new file mode 100644 index 0000000000..2065b0b9ff --- /dev/null +++ b/db/mork/src/morkHandle.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKHANDLE_ +#define _MORKHANDLE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkPool; +class morkObject; +class morkFactory; + +#define morkDerived_kHandle /*i*/ 0x486E /* ascii 'Hn' */ +#define morkHandle_kTag 0x68416E44 /* ascii 'hAnD' */ + +/*| morkHandle: +|*/ +class morkHandle : public morkNode { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + + mork_u4 mHandle_Tag; // must equal morkHandle_kTag + morkEnv* mHandle_Env; // pool that allocated this handle + morkHandleFace* mHandle_Face; // cookie from pool containing this + morkObject* mHandle_Object; // object this handle wraps for MDB API + mork_magic mHandle_Magic; // magic sig different in each subclass + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseHandle() only if open + virtual ~morkHandle(); // assert that CloseHandle() executed earlier + +public: // morkHandle construction & destruction + morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool + morkHandleFace* ioFace, // must not be nil, cookie for this handle + morkObject* ioObject, // must not be nil, the object for this handle + mork_magic inMagic); // magic sig to denote specific subclass + void CloseHandle(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkHandle(const morkHandle& other); + morkHandle& operator=(const morkHandle& other); + +protected: // special case empty construction for morkHandleFrame + friend class morkHandleFrame; + morkHandle() { } + +public: // dynamic type identification + mork_bool IsHandle() const + { return IsNode() && mNode_Derived == morkDerived_kHandle; } +// } ===== end morkNode methods ===== + +public: // morkHandle memory management operators + void* operator new(size_t inSize, morkPool& ioPool, morkZone& ioZone, morkEnv* ev) CPP_THROW_NEW + { return ioPool.NewHandle(ev, inSize, &ioZone); } + + void* operator new(size_t inSize, morkPool& ioPool, morkEnv* ev) CPP_THROW_NEW + { return ioPool.NewHandle(ev, inSize, (morkZone*) 0); } + + void* operator new(size_t inSize, morkHandleFace* ioFace) CPP_THROW_NEW + { MORK_USED_1(inSize); return ioFace; } + + +public: // other handle methods + mork_bool GoodHandleTag() const + { return mHandle_Tag == morkHandle_kTag; } + + void NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const; + void NewDownHandleError(morkEnv* ev) const; + void NilFactoryError(morkEnv* ev) const; + void NilHandleObjectError(morkEnv* ev) const; + void NonNodeObjectError(morkEnv* ev) const; + void NonOpenObjectError(morkEnv* ev) const; + + morkObject* GetGoodHandleObject(morkEnv* ev, + mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const; + +public: // interface supporting mdbObject methods + + morkEnv* CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable, + mork_bool inClosedOkay, nsresult* outErr) const; + + // { ----- begin mdbObject style methods ----- + nsresult Handle_IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly); + + nsresult Handle_GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); + nsresult Handle_GetWeakRefCount(nsIMdbEnv* ev, mdb_count* outCount); + nsresult Handle_GetStrongRefCount(nsIMdbEnv* ev, mdb_count* outCount); + + nsresult Handle_AddWeakRef(nsIMdbEnv* ev); + nsresult Handle_AddStrongRef(nsIMdbEnv* ev); + + nsresult Handle_CutWeakRef(nsIMdbEnv* ev); + nsresult Handle_CutStrongRef(nsIMdbEnv* ev); + + nsresult Handle_CloseMdbObject(nsIMdbEnv* ev); + nsresult Handle_IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen); + // } ----- end mdbObject style methods ----- + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakHandle(morkHandle* me, + morkEnv* ev, morkHandle** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongHandle(morkHandle* me, + morkEnv* ev, morkHandle** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +#define morkHandleFrame_kPadSlotCount 4 + +/*| morkHandleFrame: an object format used for allocating and maintaining +**| instances of morkHandle, with leading slots used to maintain this in a +**| linked list, and following slots to provide extra footprint that might +**| be needed by any morkHandle subclasses that include very little extra +**| space (by virtue of the fact that each morkHandle subclass is expected +**| to multiply inherit from another base class that has only abstact methods +**| for space overhead related only to some vtable representation). +|*/ +class morkHandleFrame { +public: + morkLink mHandleFrame_Link; // list storage without trampling Handle + morkHandle mHandleFrame_Handle; + mork_ip mHandleFrame_Padding[ morkHandleFrame_kPadSlotCount ]; + +public: + morkHandle* AsHandle() { return &mHandleFrame_Handle; } + + morkHandleFrame() {} // actually, morkHandleFrame never gets constructed + +private: // copying is not allowed + morkHandleFrame(const morkHandleFrame& other); + morkHandleFrame& operator=(const morkHandleFrame& other); +}; + +#define morkHandleFrame_kHandleOffset \ + mork_OffsetOf(morkHandleFrame,mHandleFrame_Handle) + +#define morkHandle_AsHandleFrame(h) ((h)->mHandle_Block , \ + ((morkHandleFrame*) (((mork_u1*)(h)) - morkHandleFrame_kHandleOffset))) + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKHANDLE_ */ diff --git a/db/mork/src/morkIntMap.cpp b/db/mork/src/morkIntMap.cpp new file mode 100644 index 0000000000..9164b021d6 --- /dev/null +++ b/db/mork/src/morkIntMap.cpp @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +#include "morkIntMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkIntMap::CloseMorkNode(morkEnv* ev) // CloseIntMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseIntMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkIntMap::~morkIntMap() // assert CloseIntMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkIntMap::morkIntMap(morkEnv* ev, + const morkUsage& inUsage, mork_size inValSize, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges) +: morkMap(ev, inUsage, ioHeap, sizeof(mork_u4), inValSize, + morkIntMap_kStartSlotCount, ioSlotHeap, inHoldChanges) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kIntMap; +} + +/*public non-poly*/ void +morkIntMap::CloseIntMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CloseMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) +morkIntMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const +{ + MORK_USED_1(ev); + return *((const mork_u4*) inKeyA) == *((const mork_u4*) inKeyB); +} + +/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey) +morkIntMap::Hash(morkEnv* ev, const void* inKey) const +{ + MORK_USED_1(ev); + return *((const mork_u4*) inKey); +} +// } ===== end morkMap poly interface ===== + +mork_bool +morkIntMap::AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress) + // the AddInt() method return value equals ev->Good(). +{ + if ( ev->Good() ) + { + this->Put(ev, &inKey, &ioAddress, + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); + } + + return ev->Good(); +} + +mork_bool +morkIntMap::CutInt(morkEnv* ev, mork_u4 inKey) +{ + return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, + (mork_change**) 0); +} + +void* +morkIntMap::GetInt(morkEnv* ev, mork_u4 inKey) + // Note the returned val does NOT have an increase in refcount for this. +{ + void* val = 0; // old val in the map + this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0); + + return val; +} + +mork_bool +morkIntMap::HasInt(morkEnv* ev, mork_u4 inKey) + // Note the returned val does NOT have an increase in refcount for this. +{ + return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, + (mork_change**) 0); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#ifdef MORK_POINTER_MAP_IMPL + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkPointerMap::CloseMorkNode(morkEnv* ev) // ClosePointerMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->ClosePointerMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPointerMap::~morkPointerMap() // assert ClosePointerMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkPointerMap::morkPointerMap(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkMap(ev, inUsage, ioHeap, sizeof(void*), sizeof(void*), + morkPointerMap_kStartSlotCount, ioSlotHeap, + /*inHoldChanges*/ morkBool_kFalse) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kPointerMap; +} + +/*public non-poly*/ void +morkPointerMap::ClosePointerMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CloseMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // *((void**) inKeyA) == *((void**) inKeyB) +morkPointerMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const +{ + MORK_USED_1(ev); + return *((const void**) inKeyA) == *((const void**) inKeyB); +} + +/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey) +morkPointerMap::Hash(morkEnv* ev, const void* inKey) const +{ + MORK_USED_1(ev); + return *((const mork_u4*) inKey); +} +// } ===== end morkMap poly interface ===== + +mork_bool +morkPointerMap::AddPointer(morkEnv* ev, void* inKey, void* ioAddress) + // the AddPointer() method return value equals ev->Good(). +{ + if ( ev->Good() ) + { + this->Put(ev, &inKey, &ioAddress, + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); + } + + return ev->Good(); +} + +mork_bool +morkPointerMap::CutPointer(morkEnv* ev, void* inKey) +{ + return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, + (mork_change**) 0); +} + +void* +morkPointerMap::GetPointer(morkEnv* ev, void* inKey) + // Note the returned val does NOT have an increase in refcount for this. +{ + void* val = 0; // old val in the map + this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0); + + return val; +} + +mork_bool +morkPointerMap::HasPointer(morkEnv* ev, void* inKey) + // Note the returned val does NOT have an increase in refcount for this. +{ + return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0, + (mork_change**) 0); +} +#endif /*MORK_POINTER_MAP_IMPL*/ + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkIntMap.h b/db/mork/src/morkIntMap.h new file mode 100644 index 0000000000..543f7e59a0 --- /dev/null +++ b/db/mork/src/morkIntMap.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKINTMAP_ +#define _MORKINTMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kIntMap /*i*/ 0x694D /* ascii 'iM' */ + +#define morkIntMap_kStartSlotCount 256 + +/*| morkIntMap: maps mork_token -> morkNode +|*/ +class morkIntMap : public morkMap { // for mapping tokens to maps + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseIntMap() only if open + virtual ~morkIntMap(); // assert that CloseIntMap() executed earlier + +public: // morkMap construction & destruction + + // keySize for morkIntMap equals sizeof(mork_u4) + morkIntMap(morkEnv* ev, const morkUsage& inUsage, mork_size inValSize, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges); + void CloseIntMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsIntMap() const + { return IsNode() && mNode_Derived == morkDerived_kIntMap; } +// } ===== end morkNode methods ===== + +// { ===== begin morkMap poly interface ===== + virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const override; +// } ===== end morkMap poly interface ===== + +public: // other map methods + + mork_bool AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress); + // the AddInt() boolean return equals ev->Good(). + + mork_bool CutInt(morkEnv* ev, mork_u4 inKey); + // The CutInt() boolean return indicates whether removal happened. + + void* GetInt(morkEnv* ev, mork_u4 inKey); + // Note the returned node does NOT have an increase in refcount for this. + + mork_bool HasInt(morkEnv* ev, mork_u4 inKey); + // Note the returned node does NOT have an increase in refcount for this. + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#ifdef MORK_POINTER_MAP_IMPL + +#define morkDerived_kPointerMap /*i*/ 0x704D /* ascii 'pM' */ + +#define morkPointerMap_kStartSlotCount 256 + +/*| morkPointerMap: maps void* -> void* +**| +**| This pointer map class is equivalent to morkIntMap when sizeof(mork_u4) +**| equals sizeof(void*). However, when these two sizes are different, +**| then we cannot use the same hash table structure very easily without +**| being very careful about the size and usage assumptions of those +**| clients using the smaller data type. So we just go ahead and use +**| morkPointerMap for hash tables using pointer key types. +|*/ +class morkPointerMap : public morkMap { // for mapping tokens to maps + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // ClosePointerMap() only if open + virtual ~morkPointerMap(); // assert that ClosePointerMap() executed earlier + +public: // morkMap construction & destruction + + // keySize for morkPointerMap equals sizeof(mork_u4) + morkPointerMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void ClosePointerMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsPointerMap() const + { return IsNode() && mNode_Derived == morkDerived_kPointerMap; } +// } ===== end morkNode methods ===== + +// { ===== begin morkMap poly interface ===== + virtual mork_bool // *((void**) inKeyA) == *((void**) inKeyB) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const; + + virtual mork_u4 // some integer function of *((mork_u4*) inKey) + Hash(morkEnv* ev, const void* inKey) const; +// } ===== end morkMap poly interface ===== + +public: // other map methods + + mork_bool AddPointer(morkEnv* ev, void* inKey, void* ioAddress); + // the AddPointer() boolean return equals ev->Good(). + + mork_bool CutPointer(morkEnv* ev, void* inKey); + // The CutPointer() boolean return indicates whether removal happened. + + void* GetPointer(morkEnv* ev, void* inKey); + // Note the returned node does NOT have an increase in refcount for this. + + mork_bool HasPointer(morkEnv* ev, void* inKey); + // Note the returned node does NOT have an increase in refcount for this. + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakIntMap(morkIntMap* me, + morkEnv* ev, morkIntMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongIntMap(morkIntMap* me, + morkEnv* ev, morkIntMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } + +}; +#endif /*MORK_POINTER_MAP_IMPL*/ + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKINTMAP_ */ diff --git a/db/mork/src/morkMap.cpp b/db/mork/src/morkMap.cpp new file mode 100644 index 0000000000..ca6b27827a --- /dev/null +++ b/db/mork/src/morkMap.cpp @@ -0,0 +1,953 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is mostly a port to C++ from public domain IronDoc C sources. +// Note many code comments here come verbatim from cut-and-pasted IronDoc. + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + + +class morkHashArrays { +public: + nsIMdbHeap* mHashArrays_Heap; // copy of mMap_Heap + mork_count mHashArrays_Slots; // copy of mMap_Slots + + mork_u1* mHashArrays_Keys; // copy of mMap_Keys + mork_u1* mHashArrays_Vals; // copy of mMap_Vals + morkAssoc* mHashArrays_Assocs; // copy of mMap_Assocs + mork_change* mHashArrays_Changes; // copy of mMap_Changes + morkAssoc** mHashArrays_Buckets; // copy of mMap_Buckets + morkAssoc* mHashArrays_FreeList; // copy of mMap_FreeList + +public: + void finalize(morkEnv* ev); +}; + +void morkHashArrays::finalize(morkEnv* ev) +{ + nsIMdbEnv* menv = ev->AsMdbEnv(); + nsIMdbHeap* heap = mHashArrays_Heap; + + if ( heap ) + { + if ( mHashArrays_Keys ) + heap->Free(menv, mHashArrays_Keys); + if ( mHashArrays_Vals ) + heap->Free(menv, mHashArrays_Vals); + if ( mHashArrays_Assocs ) + heap->Free(menv, mHashArrays_Assocs); + if ( mHashArrays_Changes ) + heap->Free(menv, mHashArrays_Changes); + if ( mHashArrays_Buckets ) + heap->Free(menv, mHashArrays_Buckets); + } +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkMap::~morkMap() // assert CloseMap() executed earlier +{ + MORK_ASSERT(mMap_FreeList==0); + MORK_ASSERT(mMap_Buckets==0); + MORK_ASSERT(mMap_Keys==0); + MORK_ASSERT(mMap_Vals==0); + MORK_ASSERT(mMap_Changes==0); + MORK_ASSERT(mMap_Assocs==0); +} + +/*public non-poly*/ void +morkMap::CloseMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + nsIMdbHeap* heap = mMap_Heap; + if ( heap ) /* need to free the arrays? */ + { + nsIMdbEnv* menv = ev->AsMdbEnv(); + + if ( mMap_Keys ) + heap->Free(menv, mMap_Keys); + + if ( mMap_Vals ) + heap->Free(menv, mMap_Vals); + + if ( mMap_Assocs ) + heap->Free(menv, mMap_Assocs); + + if ( mMap_Buckets ) + heap->Free(menv, mMap_Buckets); + + if ( mMap_Changes ) + heap->Free(menv, mMap_Changes); + } + mMap_Keys = 0; + mMap_Vals = 0; + mMap_Buckets = 0; + mMap_Assocs = 0; + mMap_Changes = 0; + mMap_FreeList = 0; + MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +void +morkMap::clear_map(morkEnv* ev, nsIMdbHeap* ioSlotHeap) +{ + mMap_Tag = 0; + mMap_Seed = 0; + mMap_Slots = 0; + mMap_Fill = 0; + mMap_Keys = 0; + mMap_Vals = 0; + mMap_Assocs = 0; + mMap_Changes = 0; + mMap_Buckets = 0; + mMap_FreeList = 0; + MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm)); + + mMap_Heap = 0; + if ( ioSlotHeap ) + { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mMap_Heap); + } + else + ev->NilPointerError(); +} + +morkMap::morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_size inKeySize, mork_size inValSize, + mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges) +: morkNode(ev, inUsage, ioHeap) +, mMap_Heap( 0 ) +{ + if ( ev->Good() ) + { + this->clear_map(ev, ioSlotHeap); + if ( ev->Good() ) + { + mMap_Form.mMapForm_HoldChanges = inHoldChanges; + + mMap_Form.mMapForm_KeySize = inKeySize; + mMap_Form.mMapForm_ValSize = inValSize; + mMap_Form.mMapForm_KeyIsIP = ( inKeySize == sizeof(mork_ip) ); + mMap_Form.mMapForm_ValIsIP = ( inValSize == sizeof(mork_ip) ); + + this->InitMap(ev, inSlots); + if ( ev->Good() ) + mNode_Derived = morkDerived_kMap; + } + } +} + +void +morkMap::NewIterOutOfSyncError(morkEnv* ev) +{ + ev->NewError("map iter out of sync"); +} + +void morkMap::NewBadMapError(morkEnv* ev) +{ + ev->NewError("bad morkMap tag"); +} + +void morkMap::NewSlotsUnderflowWarning(morkEnv* ev) +{ + ev->NewWarning("member count underflow"); +} + +void morkMap::InitMap(morkEnv* ev, mork_size inSlots) +{ + if ( ev->Good() ) + { + morkHashArrays old; + // MORK_MEMCPY(&mMap_Form, &inForm, sizeof(morkMapForm)); + if ( inSlots < 3 ) /* requested capacity absurdly small? */ + inSlots = 3; /* bump it up to a minimum practical level */ + else if ( inSlots > (128 * 1024) ) /* requested slots absurdly big? */ + inSlots = (128 * 1024); /* decrease it to a maximum practical level */ + + if ( this->new_arrays(ev, &old, inSlots) ) + mMap_Tag = morkMap_kTag; + + MORK_MEMSET(&old, 0, sizeof(morkHashArrays)); // do NOT finalize + } +} + +morkAssoc** +morkMap::find(morkEnv* ev, const void* inKey, mork_u4 inHash) const +{ + mork_u1* keys = mMap_Keys; + mork_num keySize = this->FormKeySize(); + // morkMap_mEqual equal = this->FormEqual(); + + morkAssoc** ref = mMap_Buckets + (inHash % mMap_Slots); + morkAssoc* assoc = *ref; + while ( assoc ) /* look at another assoc in the bucket? */ + { + mork_pos i = assoc - mMap_Assocs; /* index of this assoc */ + if ( this->Equal(ev, keys + (i * keySize), inKey) ) /* found? */ + return ref; + + ref = &assoc->mAssoc_Next; /* consider next assoc slot in bucket */ + assoc = *ref; /* if this is null, then we are done */ + } + return 0; +} + +/*| get_assoc: read the key and/or value at index inPos +|*/ +void +morkMap::get_assoc(void* outKey, void* outVal, mork_pos inPos) const +{ + mork_num valSize = this->FormValSize(); + if ( valSize && outVal ) /* map holds values? caller wants the value? */ + { + const mork_u1* value = mMap_Vals + (valSize * inPos); + if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */ + *((mork_ip*) outVal) = *((const mork_ip*) value); + else + MORK_MEMCPY(outVal, value, valSize); + } + if ( outKey ) /* caller wants the key? */ + { + mork_num keySize = this->FormKeySize(); + const mork_u1* key = mMap_Keys + (keySize * inPos); + if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */ + *((mork_ip*) outKey) = *((const mork_ip*) key); + else + MORK_MEMCPY(outKey, key, keySize); + } +} + +/*| put_assoc: write the key and/or value at index inPos +|*/ +void +morkMap::put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const +{ + mork_num valSize = this->FormValSize(); + if ( valSize && inVal ) /* map holds values? caller sends a value? */ + { + mork_u1* value = mMap_Vals + (valSize * inPos); + if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */ + *((mork_ip*) value) = *((const mork_ip*) inVal); + else + MORK_MEMCPY(value, inVal, valSize); + } + if ( inKey ) /* caller sends a key? */ + { + mork_num keySize = this->FormKeySize(); + mork_u1* key = mMap_Keys + (keySize * inPos); + if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */ + *((mork_ip*) key) = *((const mork_ip*) inKey); + else + MORK_MEMCPY(key, inKey, keySize); + } +} + +void* +morkMap::clear_alloc(morkEnv* ev, mork_size inSize) +{ + void* p = 0; + nsIMdbHeap* heap = mMap_Heap; + if ( heap ) + { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p ) + { + MORK_MEMSET(p, 0, inSize); + return p; + } + } + else + ev->NilPointerError(); + + return (void*) 0; +} + +void* +morkMap::alloc(morkEnv* ev, mork_size inSize) +{ + void* p = 0; + nsIMdbHeap* heap = mMap_Heap; + if ( heap ) + { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p ) + return p; + } + else + ev->NilPointerError(); + + return (void*) 0; +} + +/*| new_keys: allocate an array of inSlots new keys filled with zero. +|*/ +mork_u1* +morkMap::new_keys(morkEnv* ev, mork_num inSlots) +{ + mork_num size = inSlots * this->FormKeySize(); + return (mork_u1*) this->clear_alloc(ev, size); +} + +/*| new_values: allocate an array of inSlots new values filled with zero. +**| When values are zero sized, we just return a null pointer. +|*/ +mork_u1* +morkMap::new_values(morkEnv* ev, mork_num inSlots) +{ + mork_u1* values = 0; + mork_num size = inSlots * this->FormValSize(); + if ( size ) + values = (mork_u1*) this->clear_alloc(ev, size); + return values; +} + +mork_change* +morkMap::new_changes(morkEnv* ev, mork_num inSlots) +{ + mork_change* changes = 0; + mork_num size = inSlots * sizeof(mork_change); + if ( size && mMap_Form.mMapForm_HoldChanges ) + changes = (mork_change*) this->clear_alloc(ev, size); + return changes; +} + +/*| new_buckets: allocate an array of inSlots new buckets filled with zero. +|*/ +morkAssoc** +morkMap::new_buckets(morkEnv* ev, mork_num inSlots) +{ + mork_num size = inSlots * sizeof(morkAssoc*); + return (morkAssoc**) this->clear_alloc(ev, size); +} + +/*| new_assocs: allocate an array of inSlots new assocs, with each assoc +**| linked together in a list with the first array element at the list head +**| and the last element at the list tail. (morkMap::grow() needs this.) +|*/ +morkAssoc* +morkMap::new_assocs(morkEnv* ev, mork_num inSlots) +{ + mork_num size = inSlots * sizeof(morkAssoc); + morkAssoc* assocs = (morkAssoc*) this->alloc(ev, size); + if ( assocs ) /* able to allocate the array? */ + { + morkAssoc* a = assocs + (inSlots - 1); /* the last array element */ + a->mAssoc_Next = 0; /* terminate tail element of the list with null */ + while ( --a >= assocs ) /* another assoc to link into the list? */ + a->mAssoc_Next = a + 1; /* each points to the following assoc */ + } + return assocs; +} + +mork_bool +morkMap::new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots) +{ + mork_bool outNew = morkBool_kFalse; + + /* see if we can allocate all the new arrays before we go any further: */ + morkAssoc** newBuckets = this->new_buckets(ev, inSlots); + morkAssoc* newAssocs = this->new_assocs(ev, inSlots); + mork_u1* newKeys = this->new_keys(ev, inSlots); + mork_u1* newValues = this->new_values(ev, inSlots); + mork_change* newChanges = this->new_changes(ev, inSlots); + + /* it is okay for newChanges to be null when changes are not held: */ + mork_bool okayChanges = ( newChanges || !this->FormHoldChanges() ); + + /* it is okay for newValues to be null when values are zero sized: */ + mork_bool okayValues = ( newValues || !this->FormValSize() ); + + if ( newBuckets && newAssocs && newKeys && okayChanges && okayValues ) + { + outNew = morkBool_kTrue; /* yes, we created all the arrays we need */ + + /* init the old hashArrays with slots from this hash table: */ + old->mHashArrays_Heap = mMap_Heap; + + old->mHashArrays_Slots = mMap_Slots; + old->mHashArrays_Keys = mMap_Keys; + old->mHashArrays_Vals = mMap_Vals; + old->mHashArrays_Assocs = mMap_Assocs; + old->mHashArrays_Buckets = mMap_Buckets; + old->mHashArrays_Changes = mMap_Changes; + + /* now replace all our array slots with the new structures: */ + ++mMap_Seed; /* note the map is now changed */ + mMap_Keys = newKeys; + mMap_Vals = newValues; + mMap_Buckets = newBuckets; + mMap_Assocs = newAssocs; + mMap_FreeList = newAssocs; /* all are free to start with */ + mMap_Changes = newChanges; + mMap_Slots = inSlots; + } + else /* free the partial set of arrays that were actually allocated */ + { + nsIMdbEnv* menv = ev->AsMdbEnv(); + nsIMdbHeap* heap = mMap_Heap; + if ( newBuckets ) + heap->Free(menv, newBuckets); + if ( newAssocs ) + heap->Free(menv, newAssocs); + if ( newKeys ) + heap->Free(menv, newKeys); + if ( newValues ) + heap->Free(menv, newValues); + if ( newChanges ) + heap->Free(menv, newChanges); + + MORK_MEMSET(old, 0, sizeof(morkHashArrays)); + } + + return outNew; +} + +/*| grow: make the map arrays bigger by 33%. The old map is completely +**| full, or else we would not have called grow() to get more space. This +**| means the free list is empty, and also means every old key and value is in +**| use in the old arrays. So every key and value must be copied to the new +**| arrays, and since they are contiguous in the old arrays, we can efficiently +**| bitwise copy them in bulk from the old arrays to the new arrays, without +**| paying any attention to the structure of the old arrays. +**| +**|| The content of the old bucket and assoc arrays need not be copied because +**| this information is going to be completely rebuilt by rehashing all the +**| keys into their new buckets, given the new larger map capacity. The new +**| bucket array from new_arrays() is assumed to contain all zeroes, which is +**| necessary to ensure the lists in each bucket stay null terminated as we +**| build the new linked lists. (Note no old bucket ordering is preserved.) +**| +**|| If the old capacity is N, then in the new arrays the assocs with indexes +**| from zero to N-1 are still allocated and must be rehashed into the map. +**| The new free list contains all the following assocs, starting with the new +**| assoc link at index N. (We call the links in the link array "assocs" +**| because allocating a link is the same as allocating the key/value pair +**| with the same index as the link.) +**| +**|| The new free list is initialized simply by pointing at the first new link +**| in the assoc array after the size of the old assoc array. This assumes +**| that FeHashTable_new_arrays() has already linked all the new assocs into a +**| list with the first at the head of the list and the last at the tail of the +**| list. So by making the free list point to the first of the new uncopied +**| assocs, the list is already well-formed. +|*/ +mork_bool morkMap::grow(morkEnv* ev) +{ + if ( mMap_Heap ) /* can we grow the map? */ + { + mork_num newSlots = (mMap_Slots * 2); /* +100% */ + morkHashArrays old; /* a place to temporarily hold all the old arrays */ + if ( this->new_arrays(ev, &old, newSlots) ) /* have more? */ + { + // morkMap_mHash hash = this->FormHash(); /* for terse loop use */ + + /* figure out the bulk volume sizes of old keys and values to move: */ + mork_num oldSlots = old.mHashArrays_Slots; /* number of old assocs */ + mork_num keyBulk = oldSlots * this->FormKeySize(); /* key volume */ + mork_num valBulk = oldSlots * this->FormValSize(); /* values */ + + /* convenient variables for new arrays that need to be rehashed: */ + morkAssoc** newBuckets = mMap_Buckets; /* new all zeroes */ + morkAssoc* newAssocs = mMap_Assocs; /* hash into buckets */ + morkAssoc* newFreeList = newAssocs + oldSlots; /* new room is free */ + mork_u1* key = mMap_Keys; /* the first key to rehash */ + --newAssocs; /* back up one before preincrement used in while loop */ + + /* move all old keys and values to the new arrays: */ + MORK_MEMCPY(mMap_Keys, old.mHashArrays_Keys, keyBulk); + if ( valBulk ) /* are values nonzero sized? */ + MORK_MEMCPY(mMap_Vals, old.mHashArrays_Vals, valBulk); + + mMap_FreeList = newFreeList; /* remaining assocs are free */ + + while ( ++newAssocs < newFreeList ) /* rehash another old assoc? */ + { + morkAssoc** top = newBuckets + (this->Hash(ev, key) % newSlots); + key += this->FormKeySize(); /* the next key to rehash */ + newAssocs->mAssoc_Next = *top; /* link to prev bucket top */ + *top = newAssocs; /* push assoc on top of bucket */ + } + ++mMap_Seed; /* note the map has changed */ + old.finalize(ev); /* remember to free the old arrays */ + } + } + else ev->OutOfMemoryError(); + + return ev->Good(); +} + + +mork_bool +morkMap::Put(morkEnv* ev, const void* inKey, const void* inVal, + void* outKey, void* outVal, mork_change** outChange) +{ + mork_bool outPut = morkBool_kFalse; + + if ( this->GoodMap() ) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if ( ref ) /* assoc was found? reuse an existing assoc slot? */ + { + outPut = morkBool_kTrue; /* inKey was indeed already inside the map */ + } + else /* assoc not found -- need to allocate a new assoc slot */ + { + morkAssoc* assoc = this->pop_free_assoc(); + if ( !assoc ) /* no slots remaining in free list? must grow map? */ + { + if ( this->grow(ev) ) /* successfully made map larger? */ + assoc = this->pop_free_assoc(); + } + if ( assoc ) /* allocated new assoc without error? */ + { + ref = mMap_Buckets + (hash % mMap_Slots); + assoc->mAssoc_Next = *ref; /* link to prev bucket top */ + *ref = assoc; /* push assoc on top of bucket */ + + ++mMap_Fill; /* one more member in the collection */ + ++mMap_Seed; /* note the map has changed */ + } + } + if ( ref ) /* did not have an error during possible growth? */ + { + mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ + if ( outPut && (outKey || outVal) ) /* copy old before cobbering? */ + this->get_assoc(outKey, outVal, i); + + this->put_assoc(inKey, inVal, i); + ++mMap_Seed; /* note the map has changed */ + + if ( outChange ) + { + if ( mMap_Changes ) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + } + } + else this->NewBadMapError(ev); + + return outPut; +} + +mork_num +morkMap::CutAll(morkEnv* ev) +{ + mork_num outCutAll = 0; + + if ( this->GoodMap() ) /* map looks good? */ + { + mork_num slots = mMap_Slots; + morkAssoc* before = mMap_Assocs - 1; /* before first member */ + morkAssoc* assoc = before + slots; /* the very last member */ + + ++mMap_Seed; /* note the map is changed */ + + /* make the assoc array a linked list headed by first & tailed by last: */ + assoc->mAssoc_Next = 0; /* null terminate the new free list */ + while ( --assoc > before ) /* another assoc to link into the list? */ + assoc->mAssoc_Next = assoc + 1; + mMap_FreeList = mMap_Assocs; /* all are free */ + + outCutAll = mMap_Fill; /* we'll cut all of them of course */ + + mMap_Fill = 0; /* the map is completely empty */ + } + else this->NewBadMapError(ev); + + return outCutAll; +} + +mork_bool +morkMap::Cut(morkEnv* ev, const void* inKey, + void* outKey, void* outVal, mork_change** outChange) +{ + mork_bool outCut = morkBool_kFalse; + + if ( this->GoodMap() ) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if ( ref ) /* found an assoc for key? */ + { + outCut = morkBool_kTrue; + morkAssoc* assoc = *ref; + mork_pos i = assoc - mMap_Assocs; /* index of assoc */ + if ( outKey || outVal ) + this->get_assoc(outKey, outVal, i); + + *ref = assoc->mAssoc_Next; /* unlink the found assoc */ + this->push_free_assoc(assoc); /* and put it in free list */ + + if ( outChange ) + { + if ( mMap_Changes ) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + + ++mMap_Seed; /* note the map has changed */ + if ( mMap_Fill ) /* the count shows nonzero members? */ + --mMap_Fill; /* one less member in the collection */ + else + this->NewSlotsUnderflowWarning(ev); + } + } + else this->NewBadMapError(ev); + + return outCut; +} + +mork_bool +morkMap::Get(morkEnv* ev, const void* inKey, + void* outKey, void* outVal, mork_change** outChange) +{ + mork_bool outGet = morkBool_kFalse; + + if ( this->GoodMap() ) /* looks good? */ + { + mork_u4 hash = this->Hash(ev, inKey); + morkAssoc** ref = this->find(ev, inKey, hash); + if ( ref ) /* found an assoc for inKey? */ + { + mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */ + outGet = morkBool_kTrue; + this->get_assoc(outKey, outVal, i); + if ( outChange ) + { + if ( mMap_Changes ) + *outChange = mMap_Changes + i; + else + *outChange = this->FormDummyChange(); + } + } + } + else this->NewBadMapError(ev); + + return outGet; +} + + +morkMapIter::morkMapIter( ) +: mMapIter_Map( 0 ) +, mMapIter_Seed( 0 ) + +, mMapIter_Bucket( 0 ) +, mMapIter_AssocRef( 0 ) +, mMapIter_Assoc( 0 ) +, mMapIter_Next( 0 ) +{ +} + +void +morkMapIter::InitMapIter(morkEnv* ev, morkMap* ioMap) +{ + mMapIter_Map = 0; + mMapIter_Seed = 0; + + mMapIter_Bucket = 0; + mMapIter_AssocRef = 0; + mMapIter_Assoc = 0; + mMapIter_Next = 0; + + if ( ioMap ) + { + if ( ioMap->GoodMap() ) + { + mMapIter_Map = ioMap; + mMapIter_Seed = ioMap->mMap_Seed; + } + else ioMap->NewBadMapError(ev); + } + else ev->NilPointerError(); +} + +morkMapIter::morkMapIter(morkEnv* ev, morkMap* ioMap) +: mMapIter_Map( 0 ) +, mMapIter_Seed( 0 ) + +, mMapIter_Bucket( 0 ) +, mMapIter_AssocRef( 0 ) +, mMapIter_Assoc( 0 ) +, mMapIter_Next( 0 ) +{ + if ( ioMap ) + { + if ( ioMap->GoodMap() ) + { + mMapIter_Map = ioMap; + mMapIter_Seed = ioMap->mMap_Seed; + } + else ioMap->NewBadMapError(ev); + } + else ev->NilPointerError(); +} + +void +morkMapIter::CloseMapIter(morkEnv* ev) +{ + MORK_USED_1(ev); + mMapIter_Map = 0; + mMapIter_Seed = 0; + + mMapIter_Bucket = 0; + mMapIter_AssocRef = 0; + mMapIter_Assoc = 0; + mMapIter_Next = 0; +} + +mork_change* +morkMapIter::First(morkEnv* ev, void* outKey, void* outVal) +{ + mork_change* outFirst = 0; + + morkMap* map = mMapIter_Map; + + if ( map && map->GoodMap() ) /* map looks good? */ + { + morkAssoc** bucket = map->mMap_Buckets; + morkAssoc** end = bucket + map->mMap_Slots; /* one past last */ + + mMapIter_Seed = map->mMap_Seed; /* sync the seeds */ + + while ( bucket < end ) /* another bucket in which to look for assocs? */ + { + morkAssoc* assoc = *bucket++; + if ( assoc ) /* found the first map assoc in use? */ + { + mork_pos i = assoc - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outFirst = ( c )? (c + i) : map->FormDummyChange(); + + mMapIter_Assoc = assoc; /* current assoc in iteration */ + mMapIter_Next = assoc->mAssoc_Next; /* more in bucket */ + mMapIter_Bucket = --bucket; /* bucket for this assoc */ + mMapIter_AssocRef = bucket; /* slot referencing assoc */ + + map->get_assoc(outKey, outVal, i); + + break; /* end while loop */ + } + } + } + else map->NewBadMapError(ev); + + return outFirst; +} + +mork_change* +morkMapIter::Next(morkEnv* ev, void* outKey, void* outVal) +{ + mork_change* outNext = 0; + + morkMap* map = mMapIter_Map; + + if ( map && map->GoodMap() ) /* map looks good? */ + { + if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if ( here ) /* iteration is not yet concluded? */ + { + morkAssoc* next = mMapIter_Next; + morkAssoc* assoc = next; /* default new mMapIter_Assoc */ + if ( next ) /* there are more assocs in the same bucket after Here? */ + { + morkAssoc** ref = mMapIter_AssocRef; + + /* (*HereRef) equals Here, except when Here has been cut, after + ** which (*HereRef) always equals Next. So if (*HereRef) is not + ** equal to Next, then HereRef still needs to be updated to point + ** somewhere else other than Here. Otherwise it is fine. + */ + if ( *ref != next ) /* Here was not cut? must update HereRef? */ + mMapIter_AssocRef = &here->mAssoc_Next; + + mMapIter_Next = next->mAssoc_Next; + } + else /* look for the next assoc in the next nonempty bucket */ + { + morkAssoc** bucket = map->mMap_Buckets; + morkAssoc** end = bucket + map->mMap_Slots; /* beyond */ + mMapIter_Assoc = 0; /* default to no more assocs */ + bucket = mMapIter_Bucket; /* last exhausted bucket */ + mMapIter_Assoc = 0; /* default to iteration ended */ + + while ( ++bucket < end ) /* another bucket to search for assocs? */ + { + assoc = *bucket; + if ( assoc ) /* found another map assoc in use? */ + { + mMapIter_Bucket = bucket; + mMapIter_AssocRef = bucket; /* ref to assoc */ + mMapIter_Next = assoc->mAssoc_Next; /* more */ + + break; /* end while loop */ + } + } + } + if ( assoc ) /* did we find another assoc in the iteration? */ + { + mMapIter_Assoc = assoc; /* current assoc */ + mork_pos i = assoc - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outNext = ( c )? (c + i) : map->FormDummyChange(); + + map->get_assoc( outKey, outVal, i); + } + } + } + else map->NewIterOutOfSyncError(ev); + } + else map->NewBadMapError(ev); + + return outNext; +} + +mork_change* +morkMapIter::Here(morkEnv* ev, void* outKey, void* outVal) +{ + mork_change* outHere = 0; + + morkMap* map = mMapIter_Map; + + if ( map && map->GoodMap() ) /* map looks good? */ + { + if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if ( here ) /* iteration is not yet concluded? */ + { + mork_pos i = here - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outHere = ( c )? (c + i) : map->FormDummyChange(); + + map->get_assoc(outKey, outVal, i); + } + } + else map->NewIterOutOfSyncError(ev); + } + else map->NewBadMapError(ev); + + return outHere; +} + +mork_change* +morkMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal) +{ + mork_change* outCutHere = 0; + morkMap* map = mMapIter_Map; + + if ( map && map->GoodMap() ) /* map looks good? */ + { + if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */ + { + morkAssoc* here = mMapIter_Assoc; /* current assoc */ + if ( here ) /* iteration is not yet concluded? */ + { + morkAssoc** ref = mMapIter_AssocRef; + if ( *ref != mMapIter_Next ) /* not already cut? */ + { + mork_pos i = here - map->mMap_Assocs; + mork_change* c = map->mMap_Changes; + outCutHere = ( c )? (c + i) : map->FormDummyChange(); + if ( outKey || outVal ) + map->get_assoc(outKey, outVal, i); + + map->push_free_assoc(here); /* add to free list */ + *ref = mMapIter_Next; /* unlink here from bucket list */ + + /* note the map has changed, but we are still in sync: */ + mMapIter_Seed = ++map->mMap_Seed; /* sync */ + + if ( map->mMap_Fill ) /* still has nonzero value? */ + --map->mMap_Fill; /* one less member in the collection */ + else + map->NewSlotsUnderflowWarning(ev); + } + } + } + else map->NewIterOutOfSyncError(ev); + } + else map->NewBadMapError(ev); + + return outCutHere; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkMap.h b/db/mork/src/morkMap.h new file mode 100644 index 0000000000..57104df0e3 --- /dev/null +++ b/db/mork/src/morkMap.h @@ -0,0 +1,394 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKMAP_ +#define _MORKMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/* (These hash methods closely resemble those in public domain IronDoc.) */ + +/*| Equal: equal for hash table. Note equal(a,b) implies hash(a)==hash(b). +|*/ +typedef mork_bool (* morkMap_mEqual) +(const morkMap* self, morkEnv* ev, const void* inKeyA, const void* inKeyB); + +/*| Hash: hash for hash table. Note equal(a,b) implies hash(a)==hash(b). +|*/ +typedef mork_u4 (* morkMap_mHash) +(const morkMap* self, morkEnv* ev, const void* inKey); + +/*| IsNil: whether a key slot contains a "null" value denoting "no such key". +|*/ +typedef mork_bool (* morkMap_mIsNil) +(const morkMap* self, morkEnv* ev, const void* inKey); + +/*| Note: notify regarding a refcounting change for a key or a value. +|*/ +//typedef void (* morkMap_mNote) +//(morkMap* self, morkEnv* ev, void* inKeyOrVal); + +/*| morkMapForm: slots need to initialize a new dict. (This is very similar +**| to the config object for public domain IronDoc hash tables.) +|*/ +class morkMapForm { // a struct of callback method pointers for morkMap +public: + + // const void* mMapForm_NilKey; // externally defined 'nil' bit pattern + + // void* mMapForm_NilBuf[ 8 ]; // potential place to put NilKey + // If keys are no larger than 8*sizeof(void*), NilKey can be put in NilBuf. + // Note this should be true for all Mork subclasses, and we plan usage so. + + // These three methods must always be provided, so zero will cause errors: + + // morkMap_mEqual mMapForm_Equal; // for comparing two keys for identity + // morkMap_mHash mMapForm_Hash; // deterministic key to hash method + // morkMap_mIsNil mMapForm_IsNil; // to query whether a key equals 'nil' + + // If any of these method slots are nonzero, then morkMap will call the + // appropriate one to notify dict users when a key or value is added or cut. + // Presumably a caller wants to know this in order to perform refcounting or + // some other kind of memory management. These methods are definitely only + // called when references to keys or values are inserted or removed, and are + // never called when the actual number of references does not change (such + // as when added keys are already present or cut keys are alreading missing). + // + // morkMap_mNote mMapForm_AddKey; // if nonzero, notify about add key + // morkMap_mNote mMapForm_CutKey; // if nonzero, notify about cut key + // morkMap_mNote mMapForm_AddVal; // if nonzero, notify about add val + // morkMap_mNote mMapForm_CutVal; // if nonzero, notify about cut val + // + // These note methods have been removed because it seems difficult to + // guarantee suitable alignment of objects passed to notification methods. + + // Note dict clients should pick key and val sizes that provide whatever + // alignment will be required for an array of such keys and values. + mork_size mMapForm_KeySize; // size of every key (cannot be zero) + mork_size mMapForm_ValSize; // size of every val (can indeed be zero) + + mork_bool mMapForm_HoldChanges; // support changes array in the map + mork_change mMapForm_DummyChange; // change used for false HoldChanges + mork_bool mMapForm_KeyIsIP; // key is mork_ip sized + mork_bool mMapForm_ValIsIP; // key is mork_ip sized +}; + +/*| morkAssoc: a canonical association slot in a morkMap. A single assoc +**| instance does nothing except point to the next assoc in the same bucket +**| of a hash table. Each assoc has only two interesting attributes: 1) the +**| address of the assoc, and 2) the next assoc in a bucket's list. The assoc +**| address is interesting because in the context of an array of such assocs, +**| one can determine the index of a particular assoc in the array by address +**| arithmetic, subtracting the array address from the assoc address. And the +**| index of each assoc is the same index as the associated key and val slots +**| in the associated arrays +**| +**|| Think of an assoc instance as really also containing a key slot and a val +**| slot, where each key is mMap_Form.mMapForm_KeySize bytes in size, and +**| each val is mMap_Form.mMapForm_ValSize in size. But the key and val +**| slots are stored in separate arrays with indexes that are parallel to the +**| indexes in the array of morkAssoc instances. We have taken the variable +**| sized slots out of the morkAssoc structure, and put them into parallel +**| arrays associated with each morkAssoc by array index. And this leaves us +**| with only the link field to the next assoc in each assoc instance. +|*/ +class morkAssoc { +public: + morkAssoc* mAssoc_Next; +}; + + +#define morkDerived_kMap /*i*/ 0x4D70 /* ascii 'Mp' */ + +#define morkMap_kTag /*i*/ 0x6D4D6150 /* ascii 'mMaP' */ + +/*| morkMap: a hash table based on the public domain IronDoc hash table +**| (which is in turn rather like a similar OpenDoc hash table). +|*/ +class morkMap : public morkNode { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + + nsIMdbHeap* mMap_Heap; // strong ref to heap allocating all space + mork_u4 mMap_Tag; // must equal morkMap_kTag + + // When a morkMap instance is constructed, the dict form slots must be + // provided in order to properly configure a dict with all runtime needs: + + morkMapForm mMap_Form; // construction time parameterization + + // Whenever the dict changes structure in a way that would affect any + // iteration of the dict associations, the seed increments to show this: + + mork_seed mMap_Seed; // counter for member and structural changes + + // The current total assoc capacity of the dict is mMap_Slots, where + // mMap_Fill of these slots are actually holding content, so mMap_Fill + // is the actual membership count, and mMap_Slots is how larger membership + // can become before the hash table must grow the buffers being used. + + mork_count mMap_Slots; // count of slots in the hash table + mork_fill mMap_Fill; // number of used slots in the hash table + + // Key and value slots are bound to corresponding mMap_Assocs array slots. + // Instead of having a single array like this: {key,val,next}[ mMap_Slots ] + // we have instead three parallel arrays with essentially the same meaning: + // {key}[ mMap_Slots ], {val}[ mMap_Slots ], {assocs}[ mMap_Slots ] + + mork_u1* mMap_Keys; // mMap_Slots * mMapForm_KeySize buffer + mork_u1* mMap_Vals; // mMap_Slots * mMapForm_ValSize buffer + + // An assoc is "used" when it appears in a bucket's linked list of assocs. + // Until an assoc is used, it appears in the FreeList linked list. Every + // assoc that becomes used goes into the bucket determined by hashing the + // key associated with a new assoc. The key associated with a new assoc + // goes in to the slot in mMap_Keys which occupies exactly the same array + // index as the array index of the used assoc in the mMap_Assocs array. + + morkAssoc* mMap_Assocs; // mMap_Slots * sizeof(morkAssoc) buffer + + // The changes array is only needed when the + + mork_change* mMap_Changes; // mMap_Slots * sizeof(mork_change) buffer + + // The Buckets array need not be the same length as the Assocs array, but we + // usually do it that way so the average bucket depth is no more than one. + // (We could pick a different policy, or make it parameterizable, but that's + // tuning we can do some other time.) + + morkAssoc** mMap_Buckets; // mMap_Slots * sizeof(morkAssoc*) buffer + + // The length of the mMap_FreeList should equal (mMap_Slots - mMap_Fill). + // We need a free list instead of a simpler representation because assocs + // can be cut and returned to availability in any kind of unknown pattern. + // (However, when assocs are first allocated, or when the dict is grown, we + // know all new assocs are contiguous and can chain together adjacently.) + + morkAssoc* mMap_FreeList; // list of unused mMap_Assocs array slots + +public: // getters (morkProbeMap compatibility) + mork_fill MapFill() const { return mMap_Fill; } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseMap() only if open + virtual ~morkMap(); // assert that CloseMap() executed earlier + +public: // morkMap construction & destruction + morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + mork_size inKeySize, mork_size inValSize, + mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges); + + void CloseMap(morkEnv* ev); // called by + +public: // dynamic type identification + mork_bool IsMap() const + { return IsNode() && mNode_Derived == morkDerived_kMap; } +// } ===== end morkNode methods ===== + +public: // poly map hash table methods + +// { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const = 0; + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const = 0; +// } ===== end morkMap poly interface ===== + +public: // open utitity methods + + mork_bool GoodMapTag() const { return mMap_Tag == morkMap_kTag; } + mork_bool GoodMap() const + { return ( IsNode() && GoodMapTag() ); } + + void NewIterOutOfSyncError(morkEnv* ev); + void NewBadMapError(morkEnv* ev); + void NewSlotsUnderflowWarning(morkEnv* ev); + void InitMap(morkEnv* ev, mork_size inSlots); + +protected: // internal utitity methods + + friend class morkMapIter; + void clear_map(morkEnv* ev, nsIMdbHeap* ioHeap); + + void* alloc(morkEnv* ev, mork_size inSize); + void* clear_alloc(morkEnv* ev, mork_size inSize); + + void push_free_assoc(morkAssoc* ioAssoc) + { + ioAssoc->mAssoc_Next = mMap_FreeList; + mMap_FreeList = ioAssoc; + } + + morkAssoc* pop_free_assoc() + { + morkAssoc* assoc = mMap_FreeList; + if ( assoc ) + mMap_FreeList = assoc->mAssoc_Next; + return assoc; + } + + morkAssoc** find(morkEnv* ev, const void* inKey, mork_u4 inHash) const; + + mork_u1* new_keys(morkEnv* ev, mork_num inSlots); + mork_u1* new_values(morkEnv* ev, mork_num inSlots); + mork_change* new_changes(morkEnv* ev, mork_num inSlots); + morkAssoc** new_buckets(morkEnv* ev, mork_num inSlots); + morkAssoc* new_assocs(morkEnv* ev, mork_num inSlots); + mork_bool new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots); + + mork_bool grow(morkEnv* ev); + + void get_assoc(void* outKey, void* outVal, mork_pos inPos) const; + void put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const; + +public: // inlines to form slots + // const void* FormNilKey() const { return mMap_Form.mMapForm_NilKey; } + + // morkMap_mEqual FormEqual() const { return mMap_Form.mMapForm_Equal; } + // morkMap_mHash FormHash() const { return mMap_Form.mMapForm_Hash; } + // orkMap_mIsNil FormIsNil() const { return mMap_Form.mMapForm_IsNil; } + + // morkMap_mNote FormAddKey() const { return mMap_Form.mMapForm_AddKey; } + // morkMap_mNote FormCutKey() const { return mMap_Form.mMapForm_CutKey; } + // morkMap_mNote FormAddVal() const { return mMap_Form.mMapForm_AddVal; } + // morkMap_mNote FormCutVal() const { return mMap_Form.mMapForm_CutVal; } + + mork_size FormKeySize() const { return mMap_Form.mMapForm_KeySize; } + mork_size FormValSize() const { return mMap_Form.mMapForm_ValSize; } + + mork_bool FormKeyIsIP() const { return mMap_Form.mMapForm_KeyIsIP; } + mork_bool FormValIsIP() const { return mMap_Form.mMapForm_ValIsIP; } + + mork_bool FormHoldChanges() const + { return mMap_Form.mMapForm_HoldChanges; } + + mork_change* FormDummyChange() + { return &mMap_Form.mMapForm_DummyChange; } + +public: // other map methods + + mork_bool Put(morkEnv* ev, const void* inKey, const void* inVal, + void* outKey, void* outVal, mork_change** outChange); + + mork_bool Cut(morkEnv* ev, const void* inKey, + void* outKey, void* outVal, mork_change** outChange); + + mork_bool Get(morkEnv* ev, const void* inKey, + void* outKey, void* outVal, mork_change** outChange); + + mork_num CutAll(morkEnv* ev); + +private: // copying is not allowed + morkMap(const morkMap& other); + morkMap& operator=(const morkMap& other); + + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakMap(morkMap* me, + morkEnv* ev, morkMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongMap(morkMap* me, + morkEnv* ev, morkMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +/*| morkMapIter: an iterator for morkMap and subclasses. This is not a node, +**| and expected usage is as a member of some other node subclass, such as in +**| a cursor subclass or a thumb subclass. Also, iters might be as temp stack +**| objects when scanning the content of a map. +|*/ +class morkMapIter{ // iterator for hash table map + +protected: + morkMap* mMapIter_Map; // map to iterate, NOT refcounted + mork_seed mMapIter_Seed; // cached copy of map's seed + + morkAssoc** mMapIter_Bucket; // one bucket in mMap_Buckets array + morkAssoc** mMapIter_AssocRef; // usually *AtRef equals Here + morkAssoc* mMapIter_Assoc; // the current assoc in an iteration + morkAssoc* mMapIter_Next; // mMapIter_Assoc->mAssoc_Next */ + +public: + morkMapIter(morkEnv* ev, morkMap* ioMap); + void CloseMapIter(morkEnv* ev); + + morkMapIter( ); // everything set to zero -- need to call InitMapIter() + +protected: // we want all subclasses to provide typesafe wrappers: + + void InitMapIter(morkEnv* ev, morkMap* ioMap); + + // The morkAssoc returned below is always either mork_change* or + // else nil (when there is no such assoc). We return a pointer to + // the change rather than a simple bool, because callers might + // want to access change info associated with an assoc. + + mork_change* First(morkEnv* ev, void* outKey, void* outVal); + mork_change* Next(morkEnv* ev, void* outKey, void* outVal); + mork_change* Here(morkEnv* ev, void* outKey, void* outVal); + + mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKMAP_ */ diff --git a/db/mork/src/morkNode.cpp b/db/mork/src/morkNode.cpp new file mode 100644 index 0000000000..823ce475be --- /dev/null +++ b/db/mork/src/morkNode.cpp @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKHANDLE_ +#include "morkHandle.h" +#endif + +/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/ + +/* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */ + +static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kHeap = morkUsage_gHeap; + +static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kStack = morkUsage_gStack; + +static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kMember = morkUsage_gMember; + +static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal; + +static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kPool = morkUsage_gPool; + +static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage() +const morkUsage& morkUsage::kNone = morkUsage_gNone; + +// This must be structured to allow for non-zero values in global variables +// just before static init time. We can only safely check for whether a +// global has the address of some other global. Please, do not initialize +// either of the variables below to zero, because this could break when a zero +// is assigned at static init time, but after EnsureReadyStaticUsage() runs. + +static mork_u4 morkUsage_g_static_init_target; // only address of this matters +static mork_u4* morkUsage_g_static_init_done; // is address of target above? + +#define morkUsage_do_static_init() \ + ( morkUsage_g_static_init_done = &morkUsage_g_static_init_target ) + +#define morkUsage_need_static_init() \ + ( morkUsage_g_static_init_done != &morkUsage_g_static_init_target ) + +/*static*/ +void morkUsage::EnsureReadyStaticUsage() +{ + if ( morkUsage_need_static_init() ) + { + morkUsage_do_static_init(); + + morkUsage_gHeap.InitUsage(morkUsage_kHeap); + morkUsage_gStack.InitUsage(morkUsage_kStack); + morkUsage_gMember.InitUsage(morkUsage_kMember); + morkUsage_gGlobal.InitUsage(morkUsage_kGlobal); + morkUsage_gPool.InitUsage(morkUsage_kPool); + morkUsage_gNone.InitUsage(morkUsage_kNone); + } +} + +/*static*/ +const morkUsage& morkUsage::GetHeap() // kHeap safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gHeap; +} + +/*static*/ +const morkUsage& morkUsage::GetStack() // kStack safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gStack; +} + +/*static*/ +const morkUsage& morkUsage::GetMember() // kMember safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gMember; +} + +/*static*/ +const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gGlobal; +} + +/*static*/ +const morkUsage& morkUsage::GetPool() // kPool safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gPool; +} + +/*static*/ +const morkUsage& morkUsage::GetNone() // kNone safe at static init time +{ + EnsureReadyStaticUsage(); + return morkUsage_gNone; +} + +morkUsage::morkUsage() +{ + if ( morkUsage_need_static_init() ) + { + morkUsage::EnsureReadyStaticUsage(); + } +} + +morkUsage::morkUsage(mork_usage code) + : mUsage_Code(code) +{ + if ( morkUsage_need_static_init() ) + { + morkUsage::EnsureReadyStaticUsage(); + } +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*static*/ void* +morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) +{ + void* node = 0; + ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void **) &node); + if ( !node ) + ev->OutOfMemoryError(); + + return node; +} + +/*public non-poly*/ void +morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap) +{ + if ( this->IsNode() ) + { + mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode + this->morkNode::~morkNode(); // first call polymorphic destructor + if ( ioHeap ) // was this node heap allocated? + ioHeap->Free(ev->AsMdbEnv(), this); + else if ( usage == morkUsage_kPool ) // mNode_Usage before ~morkNode + { + morkHandle* h = (morkHandle*) this; + if ( h->IsHandle() && h->GoodHandleTag() ) + { + if ( h->mHandle_Face ) + { + if (ev->mEnv_HandlePool) + ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); + else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool) + h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face); + } + else + ev->NilPointerError(); + } + } + } + else + this->NonNodeError(ev); +} + +/*public virtual*/ void +morkNode::CloseMorkNode(morkEnv* ev) // CloseNode() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseNode(ev); + this->MarkShut(); + } +} +NS_IMETHODIMP +morkNode::CloseMdbObject(nsIMdbEnv* mev) +{ + return morkNode::CloseMdbObject((morkEnv *) mev); +} + +nsresult morkNode::CloseMdbObject(morkEnv *ev) +{ + // if only one ref, Handle_CutStrongRef will clean up better. + if (mNode_Uses == 1) + // XXX Casting mork_uses to nsresult + return static_cast(CutStrongRef(ev)); + + nsresult outErr = NS_OK; + + if ( IsNode() && IsOpenNode() ) + { + if ( ev ) + { + CloseMorkNode(ev); + outErr = ev->AsErr(); + } + } + return outErr; +} + +/*public virtual*/ +morkNode::~morkNode() // assert that CloseNode() executed earlier +{ + MORK_ASSERT(this->IsShutNode() || IsDeadNode()); // sometimes we call destructor explictly w/o freeing object. + mNode_Access = morkAccess_kDead; + mNode_Usage = morkUsage_kNone; +} + +/*public virtual*/ +// void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open + // CloseMorkNode() is the polymorphic close method called when uses==0, + // which must do NOTHING at all when IsOpenNode() is not true. Otherwise, + // CloseMorkNode() should call a static close method specific to an object. + // Each such static close method should either call inherited static close + // methods, or else perform the consolidated effect of calling them, where + // subclasses should closely track any changes in base classes with care. + + +/*public non-poly*/ +morkNode::morkNode( mork_usage inCode ) +: mNode_Heap( 0 ) +, mNode_Base( morkBase_kNode ) +, mNode_Derived ( 0 ) // until subclass sets appropriately +, mNode_Access( morkAccess_kOpen ) +, mNode_Usage( inCode ) +, mNode_Mutable( morkAble_kEnabled ) +, mNode_Load( morkLoad_kClean ) +, mNode_Uses( 1 ) +, mNode_Refs( 1 ) +{ +} + +/*public non-poly*/ +morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap) +: mNode_Heap( ioHeap ) +, mNode_Base( morkBase_kNode ) +, mNode_Derived ( 0 ) // until subclass sets appropriately +, mNode_Access( morkAccess_kOpen ) +, mNode_Usage( inUsage.Code() ) +, mNode_Mutable( morkAble_kEnabled ) +, mNode_Load( morkLoad_kClean ) +, mNode_Uses( 1 ) +, mNode_Refs( 1 ) +{ + if ( !ioHeap && mNode_Usage == morkUsage_kHeap ) + MORK_ASSERT(ioHeap); +} + +/*public non-poly*/ +morkNode::morkNode(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap) +: mNode_Heap( ioHeap ) +, mNode_Base( morkBase_kNode ) +, mNode_Derived ( 0 ) // until subclass sets appropriately +, mNode_Access( morkAccess_kOpen ) +, mNode_Usage( inUsage.Code() ) +, mNode_Mutable( morkAble_kEnabled ) +, mNode_Load( morkLoad_kClean ) +, mNode_Uses( 1 ) +, mNode_Refs( 1 ) +{ + if ( !ioHeap && mNode_Usage == morkUsage_kHeap ) + { + this->NilHeapError(ev); + } +} + +/*protected non-poly*/ void +morkNode::RefsUnderUsesWarning(morkEnv* ev) const +{ + ev->NewError("mNode_Refs < mNode_Uses"); +} + +/*protected non-poly*/ void +morkNode::NonNodeError(morkEnv* ev) const // called when IsNode() is false +{ + ev->NewError("non-morkNode"); +} + +/*protected non-poly*/ void +morkNode::NonOpenNodeError(morkEnv* ev) const // when IsOpenNode() is false +{ + ev->NewError("non-open-morkNode"); +} + +/*protected non-poly*/ void +morkNode::NonMutableNodeError(morkEnv* ev) const // when IsMutable() is false +{ + ev->NewError("non-mutable-morkNode"); +} + +/*protected non-poly*/ void +morkNode::NilHeapError(morkEnv* ev) const // zero mNode_Heap w/ kHeap usage +{ + ev->NewError("nil mNode_Heap"); +} + +/*protected non-poly*/ void +morkNode::RefsOverflowWarning(morkEnv* ev) const // mNode_Refs overflow +{ + ev->NewWarning("mNode_Refs overflow"); +} + +/*protected non-poly*/ void +morkNode::UsesOverflowWarning(morkEnv* ev) const // mNode_Uses overflow +{ + ev->NewWarning("mNode_Uses overflow"); +} + +/*protected non-poly*/ void +morkNode::RefsUnderflowWarning(morkEnv* ev) const // mNode_Refs underflow +{ + ev->NewWarning("mNode_Refs underflow"); +} + +/*protected non-poly*/ void +morkNode::UsesUnderflowWarning(morkEnv* ev) const // mNode_Uses underflow +{ + ev->NewWarning("mNode_Uses underflow"); +} + +/*public non-poly*/ void +morkNode::CloseNode(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + this->MarkShut(); + else + this->NonNodeError(ev); +} + + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot) + // If *ioSlot is non-nil, that file is released by CutStrongRef() and + // then zeroed out. Then if self is non-nil, this is acquired by + // calling AddStrongRef(), and if the return value shows success, + // then self is put into slot *ioSlot. Note self can be nil, so we take + // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'. +{ + nsIMdbFile* file = *ioSlot; + if ( self != file ) + { + if ( file ) + { + *ioSlot = 0; + NS_RELEASE(file); + } + if (self && ev->Good()) + NS_ADDREF(*ioSlot = self); + } +} + +void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot) + // If *ioSlot is non-nil, that heap is released by CutStrongRef() and + // then zeroed out. Then if self is non-nil, self is acquired by + // calling AddStrongRef(), and if the return value shows success, + // then self is put into slot *ioSlot. Note self can be nil, so we + // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'. +{ + nsIMdbHeap* heap = *ioSlot; + if ( self != heap ) + { + if ( heap ) + *ioSlot = 0; + + if (self && ev->Good()) + *ioSlot = self; + } +} + +/*public static*/ void +morkNode::SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot) + // If *ioSlot is non-nil, that node is released by CutStrongRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddStrongRef(), and if positive is returned to show success, + // then me is put into slot *ioSlot. Note me can be nil, so we take + // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'. +{ + morkNode* node = *ioSlot; + if ( me != node ) + { + if ( node ) + { + // what if this nulls out the ev and causes asserts? + // can we move this after the CutStrongRef()? + *ioSlot = 0; + node->CutStrongRef(ev); + } + if ( me && me->AddStrongRef(ev) ) + *ioSlot = me; + } +} + +/*public static*/ void +morkNode::SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot) + // If *ioSlot is non-nil, that node is released by CutWeakRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddWeakRef(), and if positive is returned to show success, + // then me is put into slot *ioSlot. Note me can be nil, so we + // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'. +{ + morkNode* node = *ioSlot; + if ( me != node ) + { + if ( node ) + { + *ioSlot = 0; + node->CutWeakRef(ev); + } + if ( me && me->AddWeakRef(ev) ) + *ioSlot = me; + } +} + +/*public non-poly*/ mork_uses +morkNode::AddStrongRef(morkEnv* ev) +{ + mork_uses outUses = 0; + if ( this->IsNode() ) + { + mork_uses uses = mNode_Uses; + mork_refs refs = mNode_Refs; + if ( refs < uses ) // need to fix broken refs/uses relation? + { + this->RefsUnderUsesWarning(ev); + mNode_Refs = mNode_Uses = refs = uses; + } + if ( refs < morkNode_kMaxRefCount ) // not too great? + { + mNode_Refs = ++refs; + mNode_Uses = ++uses; + } + else + this->RefsOverflowWarning(ev); + + outUses = uses; + } + else + this->NonNodeError(ev); + return outUses; +} + +/*private non-poly*/ mork_bool +morkNode::cut_use_count(morkEnv* ev) // just one part of CutStrongRef() +{ + mork_bool didCut = morkBool_kFalse; + if ( this->IsNode() ) + { + mork_uses uses = mNode_Uses; + if ( uses ) // not yet zero? + mNode_Uses = --uses; + else + this->UsesUnderflowWarning(ev); + + didCut = morkBool_kTrue; + if ( !mNode_Uses ) // last use gone? time to close node? + { + if ( this->IsOpenNode() ) + { + if ( !mNode_Refs ) // no outstanding reference? + { + this->RefsUnderflowWarning(ev); + ++mNode_Refs; // prevent potential crash during close + } + this->CloseMorkNode(ev); // polymorphic self close + // (Note CutNode() is not polymorphic -- so don't call that.) + } + } + } + else + this->NonNodeError(ev); + return didCut; +} + +/*public non-poly*/ mork_uses +morkNode::CutStrongRef(morkEnv* ev) +{ + mork_refs outRefs = 0; + if ( this->IsNode() ) + { + if ( this->cut_use_count(ev) ) + outRefs = this->CutWeakRef(ev); + } + else + this->NonNodeError(ev); + + return outRefs; +} + +/*public non-poly*/ mork_refs +morkNode::AddWeakRef(morkEnv* ev) +{ + mork_refs outRefs = 0; + if ( this->IsNode() ) + { + mork_refs refs = mNode_Refs; + if ( refs < morkNode_kMaxRefCount ) // not too great? + mNode_Refs = ++refs; + else + this->RefsOverflowWarning(ev); + + outRefs = refs; + } + else + this->NonNodeError(ev); + + return outRefs; +} + +/*public non-poly*/ mork_refs +morkNode::CutWeakRef(morkEnv* ev) +{ + mork_refs outRefs = 0; + if ( this->IsNode() ) + { + mork_uses uses = mNode_Uses; + mork_refs refs = mNode_Refs; + if ( refs ) // not yet zero? + mNode_Refs = --refs; + else + this->RefsUnderflowWarning(ev); + + if ( refs < uses ) // need to fix broken refs/uses relation? + { + this->RefsUnderUsesWarning(ev); + mNode_Refs = mNode_Uses = refs = uses; + } + + outRefs = refs; + if ( !refs ) // last reference gone? time to destroy node? + this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer + } + else + this->NonNodeError(ev); + + return outRefs; +} + +static const char morkNode_kBroken[] = "broken"; + +/*public non-poly*/ const char* +morkNode::GetNodeAccessAsString() const // e.g. "open", "shut", etc. +{ + const char* outString = morkNode_kBroken; + switch( mNode_Access ) + { + case morkAccess_kOpen: outString = "open"; break; + case morkAccess_kClosing: outString = "closing"; break; + case morkAccess_kShut: outString = "shut"; break; + case morkAccess_kDead: outString = "dead"; break; + } + return outString; +} + +/*public non-poly*/ const char* +morkNode::GetNodeUsageAsString() const // e.g. "heap", "stack", etc. +{ + const char* outString = morkNode_kBroken; + switch( mNode_Usage ) + { + case morkUsage_kHeap: outString = "heap"; break; + case morkUsage_kStack: outString = "stack"; break; + case morkUsage_kMember: outString = "member"; break; + case morkUsage_kGlobal: outString = "global"; break; + case morkUsage_kPool: outString = "pool"; break; + case morkUsage_kNone: outString = "none"; break; + } + return outString; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkNode.h b/db/mork/src/morkNode.h new file mode 100644 index 0000000000..0bed96fd2b --- /dev/null +++ b/db/mork/src/morkNode.h @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKNODE_ +#define _MORKNODE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkUsage_kHeap 'h' +#define morkUsage_kStack 's' +#define morkUsage_kMember 'm' +#define morkUsage_kGlobal 'g' +#define morkUsage_kPool 'p' +#define morkUsage_kNone 'n' + +class morkUsage { +public: + mork_usage mUsage_Code; // kHeap, kStack, kMember, or kGhost + +public: + morkUsage(mork_usage inCode); + + morkUsage(); // does nothing except maybe call EnsureReadyStaticUsage() + void InitUsage( mork_usage inCode) + { mUsage_Code = inCode; } + + ~morkUsage() { } + mork_usage Code() const { return mUsage_Code; } + + static void EnsureReadyStaticUsage(); + +public: + static const morkUsage& kHeap; // morkUsage_kHeap + static const morkUsage& kStack; // morkUsage_kStack + static const morkUsage& kMember; // morkUsage_kMember + static const morkUsage& kGlobal; // morkUsage_kGlobal + static const morkUsage& kPool; // morkUsage_kPool + static const morkUsage& kNone; // morkUsage_kNone + + static const morkUsage& GetHeap(); // kHeap, safe at static init time + static const morkUsage& GetStack(); // kStack, safe at static init time + static const morkUsage& GetMember(); // kMember, safe at static init time + static const morkUsage& GetGlobal(); // kGlobal, safe at static init time + static const morkUsage& GetPool(); // kPool, safe at static init time + static const morkUsage& GetNone(); // kNone, safe at static init time + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkNode_kMaxRefCount 0x0FFFF /* count sticks if it hits this */ + +#define morkBase_kNode /*i*/ 0x4E64 /* ascii 'Nd' */ + +/*| morkNode: several groups of two-byte integers that track the basic +**| status of an object that can be used to compose in-memory graphs. +**| This is the base class for nsIMdbObject (which adds members that fit +**| the needs of an nsIMdbObject subclass). The morkNode class is also used +**| as the base class for other Mork db classes with no strong relation to +**| the MDB class hierarchy. +**| +**|| Heap: the heap in which this node was allocated, when the usage equals +**| morkUsage_kHeap to show dynamic allocation. Note this heap is NOT ref- +**| counted, because that would be too great and complex a burden for all +**| the nodes allocated in that heap. So heap users should take care to +**| understand that nodes allocated in that heap are considered protected +**| by some inclusive context in which all those nodes are allocated, and +**| that context must maintain at least one strong refcount for the heap. +**| Occasionally a node subclass will indeed wish to hold a refcounted +**| reference to a heap, and possibly the same heap that is in mNode_Heap, +**| but this is always done in a separate slot that explicitly refcounts, +**| so we avoid confusing what is meant by the mNode_Heap slot. +|*/ +class morkNode /*: public nsISupports */ { // base class for constructing Mork object graphs + +public: // state is public because the entire Mork system is private + +// NS_DECL_ISUPPORTS; + nsIMdbHeap* mNode_Heap; // NON-refcounted heap pointer + + mork_base mNode_Base; // must equal morkBase_kNode + mork_derived mNode_Derived; // depends on specific node subclass + + mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + mork_able mNode_Mutable; // can this node be modified? + mork_load mNode_Load; // is this node clean or dirty? + + mork_uses mNode_Uses; // refcount for strong refs + mork_refs mNode_Refs; // refcount for strong refs + weak refs + +protected: // special case empty construction for morkHandleFrame + friend class morkHandleFrame; + morkNode() { } + +public: // inlines for weird mNode_Mutable and mNode_Load constants + + void SetFrozen() { mNode_Mutable = morkAble_kDisabled; } + void SetMutable() { mNode_Mutable = morkAble_kEnabled; } + void SetAsleep() { mNode_Mutable = morkAble_kAsleep; } + + mork_bool IsFrozen() const { return mNode_Mutable == morkAble_kDisabled; } + mork_bool IsMutable() const { return mNode_Mutable == morkAble_kEnabled; } + mork_bool IsAsleep() const { return mNode_Mutable == morkAble_kAsleep; } + + void SetNodeClean() { mNode_Load = morkLoad_kClean; } + void SetNodeDirty() { mNode_Load = morkLoad_kDirty; } + + mork_bool IsNodeClean() const { return mNode_Load == morkLoad_kClean; } + mork_bool IsNodeDirty() const { return mNode_Load == morkLoad_kDirty; } + +public: // morkNode memory management methods + static void* MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev); + + void ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap); // replaces operator delete() + // this->morkNode::~morkNode(); // first call polymorphic destructor + // if ( ioHeap ) // was this node heap allocated? + // ioHeap->Free(ev->AsMdbEnv(), this); + +public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkNode::MakeNew(inSize, ioHeap, ev); } + + +protected: // construction without an anv needed for first env constructed: + morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap); + + morkNode(mork_usage inCode); // usage == inCode, heap == nil + +// { ===== begin basic node interface ===== +public: // morkNode virtual methods + // virtual FlushMorkNode(morkEnv* ev, morkStream* ioStream); + // virtual WriteMorkNode(morkEnv* ev, morkStream* ioStream); + + virtual ~morkNode(); // assert that CloseNode() executed earlier + virtual void CloseMorkNode(morkEnv* ev); // CloseNode() only if open + + // CloseMorkNode() is the polymorphic close method called when uses==0, + // which must do NOTHING at all when IsOpenNode() is not true. Otherwise, + // CloseMorkNode() should call a static close method specific to an object. + // Each such static close method should either call inherited static close + // methods, or else perform the consolidated effect of calling them, where + // subclasses should closely track any changes in base classes with care. + +public: // morkNode construction + morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseNode(morkEnv* ev); // called by CloseMorkNode(); + nsresult CloseMdbObject(morkEnv *ev); + NS_IMETHOD CloseMdbObject(nsIMdbEnv *ev); +private: // copying is not allowed + morkNode(const morkNode& other); + morkNode& operator=(const morkNode& other); + +public: // dynamic type identification + mork_bool IsNode() const + { return mNode_Base == morkBase_kNode; } +// } ===== end basic node methods ===== + +public: // public error & warning methods + + void RefsUnderUsesWarning(morkEnv* ev) const; // call if mNode_Refs < mNode_Uses + void NonNodeError(morkEnv* ev) const; // call when IsNode() is false + void NilHeapError(morkEnv* ev) const; // zero mNode_Heap when usage is kHeap + void NonOpenNodeError(morkEnv* ev) const; // call when IsOpenNode() is false + + void NonMutableNodeError(morkEnv* ev) const; // when IsMutable() is false + + void RefsOverflowWarning(morkEnv* ev) const; // call on mNode_Refs overflow + void UsesOverflowWarning(morkEnv* ev) const; // call on mNode_Uses overflow + void RefsUnderflowWarning(morkEnv* ev) const; // call on mNode_Refs underflow + void UsesUnderflowWarning(morkEnv* ev) const; // call on mNode_Uses underflow + +private: // private refcounting methods + mork_bool cut_use_count(morkEnv* ev); // just one part of CutStrongRef() + +public: // other morkNode methods + + mork_bool GoodRefs() const { return mNode_Refs >= mNode_Uses; } + mork_bool BadRefs() const { return mNode_Refs < mNode_Uses; } + + mork_uses StrongRefsOnly() const { return mNode_Uses; } + mork_refs WeakRefsOnly() const { return (mork_refs) ( mNode_Refs - mNode_Uses ); } + + // (this refcounting derives from public domain IronDoc node refcounts) + virtual mork_uses AddStrongRef(morkEnv* ev); + virtual mork_uses CutStrongRef(morkEnv* ev); + mork_refs AddWeakRef(morkEnv* ev); + mork_refs CutWeakRef(morkEnv* ev); + + const char* GetNodeAccessAsString() const; // e.g. "open", "shut", etc. + const char* GetNodeUsageAsString() const; // e.g. "heap", "stack", etc. + + mork_usage NodeUsage() const { return mNode_Usage; } + + mork_bool IsHeapNode() const + { return mNode_Usage == morkUsage_kHeap; } + + mork_bool IsOpenNode() const + { return mNode_Access == morkAccess_kOpen; } + + mork_bool IsShutNode() const + { return mNode_Access == morkAccess_kShut; } + + mork_bool IsDeadNode() const + { return mNode_Access == morkAccess_kDead; } + + mork_bool IsClosingNode() const + { return mNode_Access == morkAccess_kClosing; } + + mork_bool IsOpenOrClosingNode() const + { return IsOpenNode() || IsClosingNode(); } + + mork_bool HasNodeAccess() const + { return ( IsOpenNode() || IsShutNode() || IsClosingNode() ); } + + void MarkShut() { mNode_Access = morkAccess_kShut; } + void MarkClosing() { mNode_Access = morkAccess_kClosing; } + void MarkDead() { mNode_Access = morkAccess_kDead; } + +public: // refcounting for typesafe subclass inline methods + static void SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot); + // If *ioSlot is non-nil, that node is released by CutWeakRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddWeakRef(), and if positive is returned to show success, + // then this is put into slot *ioSlot. Note me can be nil, so we + // permit expression '((morkNode*) 0L)->SlotWeakNode(ev, &slot)'. + + static void SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot); + // If *ioSlot is non-nil, that node is released by CutStrongRef() and + // then zeroed out. Then if me is non-nil, this is acquired by + // calling AddStrongRef(), and if positive is returned to show success, + // then me is put into slot *ioSlot. Note me can be nil, so we take + // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'. +}; + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot); + // If *ioSlot is non-nil, that heap is released by CutStrongRef() and + // then zeroed out. Then if self is non-nil, this is acquired by + // calling AddStrongRef(), and if the return value shows success, + // then self is put into slot *ioSlot. Note self can be nil, so we take + // expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'. + +extern void // utility method very similar to morkNode::SlotStrongNode(): +nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot); + // If *ioSlot is non-nil, that file is released by CutStrongRef() and + // then zeroed out. Then if self is non-nil, this is acquired by + // calling AddStrongRef(), and if the return value shows success, + // then self is put into slot *ioSlot. Note self can be nil, so we take + // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'. + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKNODE_ */ diff --git a/db/mork/src/morkNodeMap.cpp b/db/mork/src/morkNodeMap.cpp new file mode 100644 index 0000000000..7178fbafcb --- /dev/null +++ b/db/mork/src/morkNodeMap.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +#include "morkIntMap.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkNodeMap::CloseMorkNode(morkEnv* ev) // CloseNodeMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseNodeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkNodeMap::~morkNodeMap() // assert CloseNodeMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkNodeMap::morkNodeMap(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkIntMap(ev, inUsage, /*valsize*/ sizeof(morkNode*), ioHeap, ioSlotHeap, + /*inHoldChanges*/ morkBool_kTrue) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kNodeMap; +} + +/*public non-poly*/ void +morkNodeMap::CloseNodeMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CutAllNodes(ev); + this->CloseMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +mork_bool +morkNodeMap::AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode) + // the AddNode() method return value equals ev->Good(). +{ + if ( ioNode && ev->Good() ) + { + morkNode* node = 0; // old val in the map + + mork_bool put = this->Put(ev, &inToken, &ioNode, + /*key*/ (void*) 0, &node, (mork_change**) 0); + + if ( put ) // replaced an existing value for key inToken? + { + if ( node && node != ioNode ) // need to release old node? + node->CutStrongRef(ev); + } + + if ( ev->Bad() || !ioNode->AddStrongRef(ev) ) + { + // problems adding node or increasing refcount? + this->Cut(ev, &inToken, // make sure not in map + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); + } + } + else if ( !ioNode ) + ev->NilPointerError(); + + return ev->Good(); +} + +mork_bool +morkNodeMap::CutNode(morkEnv* ev, mork_token inToken) +{ + morkNode* node = 0; // old val in the map + mork_bool outCutNode = this->Cut(ev, &inToken, + /*key*/ (void*) 0, &node, (mork_change**) 0); + if ( node ) + node->CutStrongRef(ev); + + return outCutNode; +} + +morkNode* +morkNodeMap::GetNode(morkEnv* ev, mork_token inToken) + // Note the returned node does NOT have an increase in refcount for this. +{ + morkNode* node = 0; // old val in the map + this->Get(ev, &inToken, /*key*/ (void*) 0, &node, (mork_change**) 0); + + return node; +} + +mork_num +morkNodeMap::CutAllNodes(morkEnv* ev) + // CutAllNodes() releases all the reference node values. +{ + mork_num outSlots = mMap_Slots; + mork_token key = 0; // old key token in the map + morkNode* val = 0; // old val node in the map + + mork_change* c = 0; + morkNodeMapIter i(ev, this); + for ( c = i.FirstNode(ev, &key, &val); c ; c = i.NextNode(ev, &key, &val) ) + { + if ( val ) + val->CutStrongRef(ev); + i.CutHereNode(ev, /*key*/ (mork_token*) 0, /*val*/ (morkNode**) 0); + } + + return outSlots; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkNodeMap.h b/db/mork/src/morkNodeMap.h new file mode 100644 index 0000000000..9f4f8e5197 --- /dev/null +++ b/db/mork/src/morkNodeMap.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKNODEMAP_ +#define _MORKNODEMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKINTMAP_ +#include "morkIntMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kNodeMap /*i*/ 0x6E4D /* ascii 'nM' */ + +#define morkNodeMap_kStartSlotCount 512 + +/*| morkNodeMap: maps mork_token -> morkNode +|*/ +class morkNodeMap : public morkIntMap { // for mapping tokens to nodes + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseNodeMap() only if open + virtual ~morkNodeMap(); // assert that CloseNodeMap() executed earlier + +public: // morkMap construction & destruction + morkNodeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + void CloseNodeMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsNodeMap() const + { return IsNode() && mNode_Derived == morkDerived_kNodeMap; } +// } ===== end morkNode methods ===== + +// { ===== begin morkMap poly interface ===== + // use the Equal() and Hash() for mork_u4 inherited from morkIntMap +// } ===== end morkMap poly interface ===== + +protected: // we want all subclasses to provide typesafe wrappers: + + mork_bool AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode); + // the AddNode() boolean return equals ev->Good(). + + mork_bool CutNode(morkEnv* ev, mork_token inToken); + // The CutNode() boolean return indicates whether removal happened. + + morkNode* GetNode(morkEnv* ev, mork_token inToken); + // Note the returned node does NOT have an increase in refcount for this. + + mork_num CutAllNodes(morkEnv* ev); + // CutAllNodes() releases all the reference node values. +}; + +class morkNodeMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkNodeMapIter(morkEnv* ev, morkNodeMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkNodeMapIter( ) : morkMapIter() { } + void InitNodeMapIter(morkEnv* ev, morkNodeMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* + FirstNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) + { return this->First(ev, outToken, outNode); } + + mork_change* + NextNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) + { return this->Next(ev, outToken, outNode); } + + mork_change* + HereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) + { return this->Here(ev, outToken, outNode); } + + mork_change* + CutHereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode) + { return this->CutHere(ev, outToken, outNode); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKNODEMAP_ */ diff --git a/db/mork/src/morkObject.cpp b/db/mork/src/morkObject.cpp new file mode 100644 index 0000000000..f04aff4d37 --- /dev/null +++ b/db/mork/src/morkObject.cpp @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKHANDLE_ +#include "morkHandle.h" +#endif + +#include "nsCOMPtr.h" + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +NS_IMPL_ISUPPORTS(morkObject, nsIMdbObject) + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkObject::CloseMorkNode(morkEnv* ev) // CloseObject() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkObject::~morkObject() // assert CloseObject() executed earlier +{ + if (!IsShutNode()) + CloseMorkNode(this->mMorkEnv); + MORK_ASSERT(mObject_Handle==0); +} + +/*public non-poly*/ +morkObject::morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor) +: morkBead(inUsage, ioHeap, inBeadColor) +, mObject_Handle( 0 ) +{ + mMorkEnv = nullptr; +} + +/*public non-poly*/ +morkObject::morkObject(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor, morkHandle* ioHandle) +: morkBead(ev, inUsage, ioHeap, inBeadColor) +, mObject_Handle( 0 ) +{ + mMorkEnv = ev; + if ( ev->Good() ) + { + if ( ioHandle ) + morkHandle::SlotWeakHandle(ioHandle, ev, &mObject_Handle); + + if ( ev->Good() ) + mNode_Derived = morkDerived_kObject; + } +} + +/*public non-poly*/ void +morkObject::CloseObject(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + if ( !this->IsShutNode() ) + { + if ( mObject_Handle ) + morkHandle::SlotWeakHandle((morkHandle*) 0L, ev, &mObject_Handle); + + mBead_Color = 0; // this->CloseBead(ev); + this->MarkShut(); + } + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ----- begin factory methods ----- +NS_IMETHODIMP +morkObject::GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory) +{ + nsresult rv; + nsCOMPtr obj = do_QueryInterface(mev); + if (obj) + rv = obj->GetMdbFactory(mev, acqFactory); + else + return NS_ERROR_NO_INTERFACE; + + return rv; +} +// } ----- end factory methods ----- + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkObject::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) +{ + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkObject::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) +{ + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkObject::AddWeakRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::AddWeakRef((morkEnv *) mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkObject::AddStrongRef(morkEnv* mev) +{ + return morkNode::AddStrongRef(mev); +} +#endif + +NS_IMETHODIMP_(mork_uses) +morkObject::AddStrongRef(nsIMdbEnv* mev) +{ + return morkNode::AddStrongRef((morkEnv *) mev); +} + +NS_IMETHODIMP +morkObject::CutWeakRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::CutWeakRef((morkEnv *) mev)); +} + +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkObject::CutStrongRef(morkEnv* mev) +{ + return morkNode::CutStrongRef(mev); +} +#endif + +NS_IMETHODIMP +morkObject::CutStrongRef(nsIMdbEnv* mev) +{ + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::CutStrongRef((morkEnv *) mev)); +} + +NS_IMETHODIMP +morkObject::CloseMdbObject(nsIMdbEnv* mev) +{ + return morkNode::CloseMdbObject((morkEnv *) mev); +} + +NS_IMETHODIMP +morkObject::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) +{ + *outOpen = IsOpenNode(); + return NS_OK; +} +NS_IMETHODIMP +morkObject::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly) +{ + *outIsReadonly = IsFrozen(); + return NS_OK; +} + +//void morkObject::NewNilHandleError(morkEnv* ev) // mObject_Handle is nil +//{ +// ev->NewError("nil mObject_Handle"); +//} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkObject.h b/db/mork/src/morkObject.h new file mode 100644 index 0000000000..eac478d35b --- /dev/null +++ b/db/mork/src/morkObject.h @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKOBJECT_ +#define _MORKOBJECT_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKBEAD_ +#include "morkBead.h" +#endif + +#ifndef _MORKCONFIG_ +#include "morkConfig.h" +#endif + +#ifndef _ORKINHEAP_ +#include "orkinHeap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kObject /*i*/ 0x6F42 /* ascii 'oB' */ + +/*| morkObject: subclass of morkNode that adds knowledge of db suite factory +**| and containing port to those objects that are exposed as instances of +**| nsIMdbObject in the public interface. +|*/ +class morkObject : public morkBead, public nsIMdbObject { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + +public: // state is public because the entire Mork system is private + + morkHandle* mObject_Handle; // weak ref to handle for this object + + morkEnv * mMorkEnv; // weak ref to environment this object created in. +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseObject() only if open +#ifdef MORK_DEBUG_HEAP_STATS + void operator delete(void* ioAddress, size_t size) + { + mork_u4* array = (mork_u4*) ioAddress; + array -= 3; + orkinHeap *heap = (orkinHeap *) *array; + if (heap) + heap->Free(nullptr, ioAddress); + } +#endif + + NS_DECL_ISUPPORTS + + // { ----- begin attribute methods ----- + NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override; + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin factory methods ----- + NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) override; + // } ----- end factory methods ----- + + // { ----- begin ref counting for well-behaved cyclic graphs ----- + NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs + mdb_count* outCount) override; + NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs + mdb_count* outCount) override; + + NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero + NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override; + // } ----- end ref counting ----- + + +protected: // special case construction of first env without preceding env + morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor); + virtual ~morkObject(); // assert that CloseObject() executed earlier + +public: // morkEnv construction & destruction + morkObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + mork_color inBeadColor, morkHandle* ioHandle); // ioHandle can be nil + void CloseObject(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkObject(const morkObject& other); + morkObject& operator=(const morkObject& other); + +public: // dynamic type identification + mork_bool IsObject() const + { return IsNode() && mNode_Derived == morkDerived_kObject; } +// } ===== end morkNode methods ===== + + // void NewNilHandleError(morkEnv* ev); // mObject_Handle is nil + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakObject(morkObject* me, + morkEnv* ev, morkObject** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongObject(morkObject* me, + morkEnv* ev, morkObject** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKOBJECT_ */ diff --git a/db/mork/src/morkParser.cpp b/db/mork/src/morkParser.cpp new file mode 100644 index 0000000000..65f8d6c663 --- /dev/null +++ b/db/mork/src/morkParser.cpp @@ -0,0 +1,1568 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKPARSER_ +#include "morkParser.h" +#endif + +#ifndef _MORKSTREAM_ +#include "morkStream.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKSINK_ +#include "morkSink.h" +#endif + +#ifndef _MORKCH_ +#include "morkCh.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkParser::CloseMorkNode(morkEnv* ev) // CloseParser() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseParser(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkParser::~morkParser() // assert CloseParser() executed earlier +{ + MORK_ASSERT(mParser_Heap==0); + MORK_ASSERT(mParser_Stream==0); +} + +/*public non-poly*/ +morkParser::morkParser(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, mdb_count inBytesPerParseSegment, + nsIMdbHeap* ioSlotHeap) +: morkNode(ev, inUsage, ioHeap) +, mParser_Heap( 0 ) +, mParser_Stream( 0 ) +, mParser_MoreGranularity( inBytesPerParseSegment ) +, mParser_State( morkParser_kStartState ) + +, mParser_GroupContentStartPos( 0 ) + +, mParser_TableMid( ) +, mParser_RowMid( ) +, mParser_CellMid( ) + +, mParser_InPort( morkBool_kFalse ) +, mParser_InDict( morkBool_kFalse ) +, mParser_InCell( morkBool_kFalse ) +, mParser_InMeta( morkBool_kFalse ) + +, mParser_InPortRow( morkBool_kFalse ) +, mParser_InRow( morkBool_kFalse ) +, mParser_InTable( morkBool_kFalse ) +, mParser_InGroup( morkBool_kFalse ) + +, mParser_AtomChange( morkChange_kNil ) +, mParser_CellChange( morkChange_kNil ) +, mParser_RowChange( morkChange_kNil ) +, mParser_TableChange( morkChange_kNil ) + +, mParser_Change( morkChange_kNil ) +, mParser_IsBroken( morkBool_kFalse ) +, mParser_IsDone( morkBool_kFalse ) +, mParser_DoMore( morkBool_kTrue ) + +, mParser_Mid() + +, mParser_ScopeCoil(ev, ioSlotHeap) +, mParser_ValueCoil(ev, ioSlotHeap) +, mParser_ColumnCoil(ev, ioSlotHeap) +, mParser_StringCoil(ev, ioSlotHeap) + +, mParser_ScopeSpool(ev, &mParser_ScopeCoil) +, mParser_ValueSpool(ev, &mParser_ValueCoil) +, mParser_ColumnSpool(ev, &mParser_ColumnCoil) +, mParser_StringSpool(ev, &mParser_StringCoil) + +, mParser_MidYarn(ev, morkUsage_kMember, ioSlotHeap) +{ + if ( inBytesPerParseSegment < morkParser_kMinGranularity ) + inBytesPerParseSegment = morkParser_kMinGranularity; + else if ( inBytesPerParseSegment > morkParser_kMaxGranularity ) + inBytesPerParseSegment = morkParser_kMaxGranularity; + + mParser_MoreGranularity = inBytesPerParseSegment; + + if ( ioSlotHeap && ioStream ) + { + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mParser_Heap); + morkStream::SlotStrongStream(ioStream, ev, &mParser_Stream); + + if ( ev->Good() ) + { + mParser_Tag = morkParser_kTag; + mNode_Derived = morkDerived_kParser; + } + } + else + ev->NilPointerError(); +} + +/*public non-poly*/ void +morkParser::CloseParser(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + if ( !this->IsShutNode() ) + { + mParser_ScopeCoil.CloseCoil(ev); + mParser_ValueCoil.CloseCoil(ev); + mParser_ColumnCoil.CloseCoil(ev); + mParser_StringCoil.CloseCoil(ev); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mParser_Heap); + morkStream::SlotStrongStream((morkStream*) 0, ev, &mParser_Stream); + this->MarkShut(); + } + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*protected non-poly*/ void +morkParser::NonGoodParserError(morkEnv* ev) // when GoodParserTag() is false +{ + ev->NewError("non-morkNode"); +} + +/*protected non-poly*/ void +morkParser::NonUsableParserError(morkEnv* ev) // +{ + if ( this->IsNode() ) + { + if ( this->IsOpenNode() ) + { + if ( this->GoodParserTag() ) + { + // okay + } + else + this->NonGoodParserError(ev); + } + else + this->NonOpenNodeError(ev); + } + else + this->NonNodeError(ev); +} + + +/*protected non-poly*/ void +morkParser::StartParse(morkEnv* ev) +{ + MORK_USED_1(ev); + mParser_InCell = morkBool_kFalse; + mParser_InMeta = morkBool_kFalse; + mParser_InDict = morkBool_kFalse; + mParser_InPortRow = morkBool_kFalse; + + mParser_RowMid.ClearMid(); + mParser_TableMid.ClearMid(); + mParser_CellMid.ClearMid(); + + mParser_GroupId = 0; + mParser_InPort = morkBool_kTrue; + + mParser_GroupSpan.ClearSpan(); + mParser_DictSpan.ClearSpan(); + mParser_AliasSpan.ClearSpan(); + mParser_MetaSpan.ClearSpan(); + mParser_TableSpan.ClearSpan(); + mParser_RowSpan.ClearSpan(); + mParser_CellSpan.ClearSpan(); + mParser_ColumnSpan.ClearSpan(); + mParser_SlotSpan.ClearSpan(); + + mParser_PortSpan.ClearSpan(); +} + +/*protected non-poly*/ void +morkParser::StopParse(morkEnv* ev) +{ + if ( mParser_InCell ) + { + mParser_InCell = morkBool_kFalse; + mParser_CellSpan.SetEndWithEnd(mParser_PortSpan); + this->OnCellEnd(ev, mParser_CellSpan); + } + if ( mParser_InMeta ) + { + mParser_InMeta = morkBool_kFalse; + mParser_MetaSpan.SetEndWithEnd(mParser_PortSpan); + this->OnMetaEnd(ev, mParser_MetaSpan); + } + if ( mParser_InDict ) + { + mParser_InDict = morkBool_kFalse; + mParser_DictSpan.SetEndWithEnd(mParser_PortSpan); + this->OnDictEnd(ev, mParser_DictSpan); + } + if ( mParser_InPortRow ) + { + mParser_InPortRow = morkBool_kFalse; + mParser_RowSpan.SetEndWithEnd(mParser_PortSpan); + this->OnPortRowEnd(ev, mParser_RowSpan); + } + if ( mParser_InRow ) + { + mParser_InRow = morkBool_kFalse; + mParser_RowMid.ClearMid(); + mParser_RowSpan.SetEndWithEnd(mParser_PortSpan); + this->OnRowEnd(ev, mParser_RowSpan); + } + if ( mParser_InTable ) + { + mParser_InTable = morkBool_kFalse; + mParser_TableMid.ClearMid(); + mParser_TableSpan.SetEndWithEnd(mParser_PortSpan); + this->OnTableEnd(ev, mParser_TableSpan); + } + if ( mParser_GroupId ) + { + mParser_GroupId = 0; + mParser_GroupSpan.SetEndWithEnd(mParser_PortSpan); + this->OnGroupAbortEnd(ev, mParser_GroupSpan); + } + if ( mParser_InPort ) + { + mParser_InPort = morkBool_kFalse; + this->OnPortEnd(ev, mParser_PortSpan); + } +} + +int morkParser::eat_comment(morkEnv* ev) // last char was '/' +{ + morkStream* s = mParser_Stream; + // Note morkStream::Getc() returns EOF when an error occurs, so + // we don't need to check for both c != EOF and ev->Good() below. + + int c = s->Getc(ev); + if ( c == '/' ) // C++ style comment? + { + while ( (c = s->Getc(ev)) != EOF && c != 0xA && c != 0xD ) + /* empty */; + + if ( c == 0xA || c == 0xD ) + c = this->eat_line_break(ev, c); + } + else if ( c == '*' ) /* C style comment? */ + { + int depth = 1; // count depth of comments until depth reaches zero + + while ( depth > 0 && c != EOF ) // still looking for comment end(s)? + { + while ( (c = s->Getc(ev)) != EOF && c != '/' && c != '*' ) + { + if ( c == 0xA || c == 0xD ) // need to count a line break? + { + c = this->eat_line_break(ev, c); + if ( c == '/' || c == '*' ) + break; // end while loop + } + } + + if ( c == '*' ) // maybe end of a comment, if next char is '/'? + { + if ( (c = s->Getc(ev)) == '/' ) // end of comment? + { + --depth; // depth of comments has decreased by one + if ( !depth ) // comments all done? + c = s->Getc(ev); // return the byte after end of comment + } + else if ( c != EOF ) // need to put the char back? + s->Ungetc(c); // especially need to put back '*', 0xA, or 0xD + } + else if ( c == '/' ) // maybe nested comemnt, if next char is '*'? + { + if ( (c = s->Getc(ev)) == '*' ) // nested comment? + ++depth; // depth of comments has increased by one + else if ( c != EOF ) // need to put the char back? + s->Ungetc(c); // especially need to put back '/', 0xA, or 0xD + } + + if ( ev->Bad() ) + c = EOF; + } + if ( c == EOF && depth > 0 ) + ev->NewWarning("EOF before end of comment"); + } + else + ev->NewWarning("expected / or *"); + + return c; +} + +int morkParser::eat_line_break(morkEnv* ev, int inLast) +{ + morkStream* s = mParser_Stream; + int c = s->Getc(ev); // get next char after 0xA or 0xD + this->CountLineBreak(); + if ( c == 0xA || c == 0xD ) // another line break character? + { + if ( c != inLast ) // not the same as the last one? + c = s->Getc(ev); // get next char after two-byte linebreak + } + return c; +} + +int morkParser::eat_line_continue(morkEnv* ev) // last char was '\' +{ + morkStream* s = mParser_Stream; + int c = s->Getc(ev); + if ( c == 0xA || c == 0xD ) // linebreak follows \ as expected? + { + c = this->eat_line_break(ev, c); + } + else + ev->NewWarning("expected linebreak"); + + return c; +} + +int morkParser::NextChar(morkEnv* ev) // next non-white content +{ + morkStream* s = mParser_Stream; + int c = s->Getc(ev); + while ( c > 0 && ev->Good() ) + { + if ( c == '/' ) + c = this->eat_comment(ev); + else if ( c == 0xA || c == 0xD ) + c = this->eat_line_break(ev, c); + else if ( c == '\\' ) + c = this->eat_line_continue(ev); + else if ( morkCh_IsWhite(c) ) + c = s->Getc(ev); + else + break; // end while loop when return c is acceptable + } + if ( ev->Bad() ) + { + mParser_State = morkParser_kBrokenState; + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + mParser_IsBroken = morkBool_kTrue; + c = EOF; + } + else if ( c == EOF ) + { + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + } + return c; +} + +void +morkParser::OnCellState(morkEnv* ev) +{ + ev->StubMethodOnlyError(); +} + +void +morkParser::OnMetaState(morkEnv* ev) +{ + ev->StubMethodOnlyError(); +} + +void +morkParser::OnRowState(morkEnv* ev) +{ + ev->StubMethodOnlyError(); +} + +void +morkParser::OnTableState(morkEnv* ev) +{ + ev->StubMethodOnlyError(); +} + +void +morkParser::OnDictState(morkEnv* ev) +{ + ev->StubMethodOnlyError(); +} + +morkBuf* morkParser::ReadName(morkEnv* ev, int c) +{ + morkBuf* outBuf = 0; + + if ( !morkCh_IsName(c) ) + ev->NewError("not a name char"); + + morkCoil* coil = &mParser_ColumnCoil; + coil->ClearBufFill(); + + morkSpool* spool = &mParser_ColumnSpool; + spool->Seek(ev, /*pos*/ 0); + + if ( ev->Good() ) + { + spool->Putc(ev, c); + + morkStream* s = mParser_Stream; + while ( (c = s->Getc(ev)) != EOF && morkCh_IsMore(c) && ev->Good() ) + spool->Putc(ev, c); + + if ( ev->Good() ) + { + if ( c != EOF ) + { + s->Ungetc(c); + spool->FlushSink(ev); // update coil->mBuf_Fill + } + else + this->UnexpectedEofError(ev); + + if ( ev->Good() ) + outBuf = coil; + } + } + return outBuf; +} + +mork_bool +morkParser::ReadMid(morkEnv* ev, morkMid* outMid) +{ + outMid->ClearMid(); + + morkStream* s = mParser_Stream; + int next; + outMid->mMid_Oid.mOid_Id = this->ReadHex(ev, &next); + int c = next; + if ( c == ':' ) + { + if ( (c = s->Getc(ev)) != EOF && ev->Good() ) + { + if ( c == '^' ) + { + outMid->mMid_Oid.mOid_Scope = this->ReadHex(ev, &next); + if ( ev->Good() ) + s->Ungetc(next); + } + else if ( morkCh_IsName(c) ) + { + outMid->mMid_Buf = this->ReadName(ev, c); + } + else + ev->NewError("expected name or hex after ':' following ID"); + } + + if ( c == EOF && ev->Good() ) + this->UnexpectedEofError(ev); + } + else + s->Ungetc(c); + + return ev->Good(); +} + +void +morkParser::ReadCell(morkEnv* ev) +{ + mParser_CellMid.ClearMid(); + // this->StartSpanOnLastByte(ev, &mParser_CellSpan); + morkMid* cellMid = 0; // if mid syntax is used for column + morkBuf* cellBuf = 0; // if naked string is used for column + + morkStream* s = mParser_Stream; + int c; + if ( (c = s->Getc(ev)) != EOF && ev->Good() ) + { + // this->StartSpanOnLastByte(ev, &mParser_ColumnSpan); + if ( c == '^' ) + { + cellMid = &mParser_CellMid; + this->ReadMid(ev, cellMid); + // if ( !mParser_CellMid.mMid_Oid.mOid_Scope ) + // mParser_CellMid.mMid_Oid.mOid_Scope = (mork_scope) 'c'; + } + else + { + if (mParser_InMeta && c == morkStore_kFormColumn) + { + ReadCellForm(ev, c); + return; + } + else + cellBuf = this->ReadName(ev, c); + } + if ( ev->Good() ) + { + // this->EndSpanOnThisByte(ev, &mParser_ColumnSpan); + + mParser_InCell = morkBool_kTrue; + this->OnNewCell(ev, *mParser_CellSpan.AsPlace(), + cellMid, cellBuf); // , mParser_CellChange + + mParser_CellChange = morkChange_kNil; + if ( (c = this->NextChar(ev)) != EOF && ev->Good() ) + { + // this->StartSpanOnLastByte(ev, &mParser_SlotSpan); + if ( c == '=' ) + { + morkBuf* buf = this->ReadValue(ev); + if ( buf ) + { + // this->EndSpanOnThisByte(ev, &mParser_SlotSpan); + this->OnValue(ev, mParser_SlotSpan, *buf); + } + } + else if ( c == '^' ) + { + if ( this->ReadMid(ev, &mParser_Mid) ) + { + // this->EndSpanOnThisByte(ev, &mParser_SlotSpan); + if ( (c = this->NextChar(ev)) != EOF && ev->Good() ) + { + if ( c != ')' ) + ev->NewError("expected ')' after cell ^ID value"); + } + else if ( c == EOF ) + this->UnexpectedEofError(ev); + + if ( ev->Good() ) + this->OnValueMid(ev, mParser_SlotSpan, mParser_Mid); + } + } + else if ( c == 'r' || c == 't' || c == '"' || c == '\'' ) + { + ev->NewError("cell syntax not yet supported"); + } + else + { + ev->NewError("unknown cell syntax"); + } + } + + // this->EndSpanOnThisByte(ev, &mParser_CellSpan); + mParser_InCell = morkBool_kFalse; + this->OnCellEnd(ev, mParser_CellSpan); + } + } + mParser_CellChange = morkChange_kNil; + + if ( c == EOF && ev->Good() ) + this->UnexpectedEofError(ev); +} + +void morkParser::ReadRowPos(morkEnv* ev) +{ + int c; // next character + mork_pos rowPos = this->ReadHex(ev, &c); + + if ( ev->Good() && c != EOF ) // should put back byte after hex? + mParser_Stream->Ungetc(c); + + this->OnRowPos(ev, rowPos); +} + +void morkParser::ReadRow(morkEnv* ev, int c) +// zm:Row ::= zm:S? '[' zm:S? zm:Id zm:RowItem* zm:S? ']' +// zm:RowItem ::= zm:MetaRow | zm:Cell +// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */ +// zm:Cell ::= zm:S? '(' zm:Column zm:S? zm:Slot? ')' +{ + if ( ev->Good() ) + { + // this->StartSpanOnLastByte(ev, &mParser_RowSpan); + if ( mParser_Change ) + mParser_RowChange = mParser_Change; + + mork_bool cutAllRowCols = morkBool_kFalse; + + if ( c == '[' ) + { + if ( ( c = this->NextChar(ev) ) == '-' ) + cutAllRowCols = morkBool_kTrue; + else if ( ev->Good() && c != EOF ) + mParser_Stream->Ungetc(c); + + if ( this->ReadMid(ev, &mParser_RowMid) ) + { + mParser_InRow = morkBool_kTrue; + this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), + mParser_RowMid, cutAllRowCols); + + mParser_Change = mParser_RowChange = morkChange_kNil; + + while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != ']' ) + { + switch ( c ) + { + case '(': // cell + this->ReadCell(ev); + break; + + case '[': // meta + this->ReadMeta(ev, ']'); + break; + + // case '+': // plus + // mParser_CellChange = morkChange_kAdd; + // break; + + case '-': // minus + // mParser_CellChange = morkChange_kCut; + this->OnMinusCell(ev); + break; + + // case '!': // bang + // mParser_CellChange = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in row"); + break; + } // switch + } // while + + if ( ev->Good() ) + { + if ( (c = this->NextChar(ev)) == '!' ) + this->ReadRowPos(ev); + else if ( c != EOF && ev->Good() ) + mParser_Stream->Ungetc(c); + } + + // this->EndSpanOnThisByte(ev, &mParser_RowSpan); + mParser_InRow = morkBool_kFalse; + this->OnRowEnd(ev, mParser_RowSpan); + + } // if ReadMid + } // if '[' + + else // c != '[' + { + morkStream* s = mParser_Stream; + s->Ungetc(c); + if ( this->ReadMid(ev, &mParser_RowMid) ) + { + mParser_InRow = morkBool_kTrue; + this->OnNewRow(ev, *mParser_RowSpan.AsPlace(), + mParser_RowMid, cutAllRowCols); + + mParser_Change = mParser_RowChange = morkChange_kNil; + + if ( ev->Good() ) + { + if ( (c = this->NextChar(ev)) == '!' ) + this->ReadRowPos(ev); + else if ( c != EOF && ev->Good() ) + s->Ungetc(c); + } + + // this->EndSpanOnThisByte(ev, &mParser_RowSpan); + mParser_InRow = morkBool_kFalse; + this->OnRowEnd(ev, mParser_RowSpan); + } + } + } + + if ( ev->Bad() ) + mParser_State = morkParser_kBrokenState; + else if ( c == EOF ) + mParser_State = morkParser_kDoneState; +} + +void morkParser::ReadTable(morkEnv* ev) +// zm:Table ::= zm:S? '{' zm:S? zm:Id zm:TableItem* zm:S? '}' +// zm:TableItem ::= zm:MetaTable | zm:RowRef | zm:Row +// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */ +{ + // this->StartSpanOnLastByte(ev, &mParser_TableSpan); + + if ( mParser_Change ) + mParser_TableChange = mParser_Change; + + mork_bool cutAllTableRows = morkBool_kFalse; + + int c = this->NextChar(ev); + if ( c == '-' ) + cutAllTableRows = morkBool_kTrue; + else if ( ev->Good() && c != EOF ) + mParser_Stream->Ungetc(c); + + if ( ev->Good() && this->ReadMid(ev, &mParser_TableMid) ) + { + mParser_InTable = morkBool_kTrue; + this->OnNewTable(ev, *mParser_TableSpan.AsPlace(), + mParser_TableMid, cutAllTableRows); + + mParser_Change = mParser_TableChange = morkChange_kNil; + + while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '}' ) + { + if ( morkCh_IsHex(c) ) + { + this->ReadRow(ev, c); + } + else + { + switch ( c ) + { + case '[': // row + this->ReadRow(ev, '['); + break; + + case '{': // meta + this->ReadMeta(ev, '}'); + break; + + // case '+': // plus + // mParser_RowChange = morkChange_kAdd; + // break; + + case '-': // minus + // mParser_RowChange = morkChange_kCut; + this->OnMinusRow(ev); + break; + + // case '!': // bang + // mParser_RowChange = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in table"); + break; + } + } + } + + // this->EndSpanOnThisByte(ev, &mParser_TableSpan); + mParser_InTable = morkBool_kFalse; + this->OnTableEnd(ev, mParser_TableSpan); + + if ( ev->Bad() ) + mParser_State = morkParser_kBrokenState; + else if ( c == EOF ) + mParser_State = morkParser_kDoneState; + } +} + +mork_id morkParser::ReadHex(morkEnv* ev, int* outNextChar) +// zm:Hex ::= [0-9a-fA-F] /* a single hex digit */ +// zm:Hex+ ::= zm:Hex | zm:Hex zm:Hex+ +{ + mork_id hex = 0; + + morkStream* s = mParser_Stream; + int c = this->NextChar(ev); + + if ( ev->Good() ) + { + if ( c != EOF ) + { + if ( morkCh_IsHex(c) ) + { + do + { + if ( morkCh_IsDigit(c) ) // '0' through '9'? + c -= '0'; + else if ( morkCh_IsUpper(c) ) // 'A' through 'F'? + c -= ('A' - 10) ; // c = (c - 'A') + 10; + else // 'a' through 'f'? + c -= ('a' - 10) ; // c = (c - 'a') + 10; + + hex = (hex << 4) + c; + } + while ( (c = s->Getc(ev)) != EOF && ev->Good() && morkCh_IsHex(c) ); + } + else + this->ExpectedHexDigitError(ev, c); + } + } + if ( c == EOF ) + this->EofInsteadOfHexError(ev); + + *outNextChar = c; + return hex; +} + +/*static*/ void +morkParser::EofInsteadOfHexError(morkEnv* ev) +{ + ev->NewWarning("eof instead of hex"); +} + +/*static*/ void +morkParser::ExpectedHexDigitError(morkEnv* ev, int c) +{ + MORK_USED_1(c); + ev->NewWarning("expected hex digit"); +} + +/*static*/ void +morkParser::ExpectedEqualError(morkEnv* ev) +{ + ev->NewWarning("expected '='"); +} + +/*static*/ void +morkParser::UnexpectedEofError(morkEnv* ev) +{ + ev->NewWarning("unexpected eof"); +} + + +morkBuf* morkParser::ReadValue(morkEnv* ev) +{ + morkBuf* outBuf = 0; + + morkCoil* coil = &mParser_ValueCoil; + coil->ClearBufFill(); + + morkSpool* spool = &mParser_ValueSpool; + spool->Seek(ev, /*pos*/ 0); + + if ( ev->Good() ) + { + morkStream* s = mParser_Stream; + int c; + while ( (c = s->Getc(ev)) != EOF && c != ')' && ev->Good() ) + { + if ( c == '\\' ) // next char is escaped by '\'? + { + if ( (c = s->Getc(ev)) == 0xA || c == 0xD ) // linebreak after \? + { + c = this->eat_line_break(ev, c); + if ( c == ')' || c == '\\' || c == '$' ) + { + s->Ungetc(c); // just let while loop test read this again + continue; // goto next iteration of while loop + } + } + if ( c == EOF || ev->Bad() ) + break; // end while loop + } + else if ( c == '$' ) // "$" escapes next two hex digits? + { + if ( (c = s->Getc(ev)) != EOF && ev->Good() ) + { + mork_ch first = (mork_ch) c; // first hex digit + if ( (c = s->Getc(ev)) != EOF && ev->Good() ) + { + mork_ch second = (mork_ch) c; // second hex digit + c = ev->HexToByte(first, second); + } + else + break; // end while loop + } + else + break; // end while loop + } + spool->Putc(ev, c); + } + + if ( ev->Good() ) + { + if ( c != EOF ) + spool->FlushSink(ev); // update coil->mBuf_Fill + else + this->UnexpectedEofError(ev); + + if ( ev->Good() ) + outBuf = coil; + } + } + return outBuf; +} + +void morkParser::ReadDictForm(morkEnv *ev) +{ + int nextChar; + nextChar = this->NextChar(ev); + if (nextChar == '(') + { + nextChar = this->NextChar(ev); + if (nextChar == morkStore_kFormColumn) + { + int dictForm; + + nextChar = this->NextChar(ev); + if (nextChar == '=') + { + dictForm = this->NextChar(ev); + nextChar = this->NextChar(ev); + } + else if (nextChar == '^') + { + dictForm = this->ReadHex(ev, &nextChar); + } + else + { + ev->NewWarning("unexpected byte in dict form"); + return; + } + mParser_ValueCoil.mText_Form = dictForm; + if (nextChar == ')') + { + nextChar = this->NextChar(ev); + if (nextChar == '>') + return; + } + } + } + ev->NewWarning("unexpected byte in dict form"); +} + +void morkParser::ReadCellForm(morkEnv *ev, int c) +{ + MORK_ASSERT (c == morkStore_kFormColumn); + int nextChar; + nextChar = this->NextChar(ev); + int cellForm; + + if (nextChar == '=') + { + cellForm = this->NextChar(ev); + nextChar = this->NextChar(ev); + } + else if (nextChar == '^') + { + cellForm = this->ReadHex(ev, &nextChar); + } + else + { + ev->NewWarning("unexpected byte in cell form"); + return; + } + // ### not sure about this. Which form should we set? + // mBuilder_CellForm = mBuilder_RowForm = cellForm; + if (nextChar == ')') + { + OnCellForm(ev, cellForm); + return; + } + ev->NewWarning("unexpected byte in cell form"); +} + +void morkParser::ReadAlias(morkEnv* ev) +// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')' +// zm:Value ::= '=' ([^)$\] | '\' zm:NonCRLF | zm:Continue | zm:Dollar)* +{ + // this->StartSpanOnLastByte(ev, &mParser_AliasSpan); + + int nextChar; + mork_id hex = this->ReadHex(ev, &nextChar); + int c = nextChar; + + mParser_Mid.ClearMid(); + mParser_Mid.mMid_Oid.mOid_Id = hex; + + if ( morkCh_IsWhite(c) && ev->Good() ) + c = this->NextChar(ev); + + if ( ev->Good() ) + { + if ( c == '<') + { + ReadDictForm(ev); + if (ev->Good()) + c = this->NextChar(ev); + } + if ( c == '=' ) + { + mParser_Mid.mMid_Buf = this->ReadValue(ev); + if ( mParser_Mid.mMid_Buf ) + { + // this->EndSpanOnThisByte(ev, &mParser_AliasSpan); + this->OnAlias(ev, mParser_AliasSpan, mParser_Mid); + // need to reset this somewhere. + mParser_ValueCoil.mText_Form = 0; + + } + } + else + this->ExpectedEqualError(ev); + } +} + +void morkParser::ReadMeta(morkEnv* ev, int inEndMeta) +// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */ +// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */ +// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */ +{ + // this->StartSpanOnLastByte(ev, &mParser_MetaSpan); + mParser_InMeta = morkBool_kTrue; + this->OnNewMeta(ev, *mParser_MetaSpan.AsPlace()); + + mork_bool more = morkBool_kTrue; // until end meta + int c; + while ( more && (c = this->NextChar(ev)) != EOF && ev->Good() ) + { + switch ( c ) + { + case '(': // cell + this->ReadCell(ev); + break; + + case '>': // maybe end meta? + if ( inEndMeta == '>' ) + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case '}': // maybe end meta? + if ( inEndMeta == '}' ) + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case ']': // maybe end meta? + if ( inEndMeta == ']' ) + more = morkBool_kFalse; // stop reading meta + else + this->UnexpectedByteInMetaWarning(ev); + break; + + case '[': // maybe table meta row? + if ( mParser_InTable ) + this->ReadRow(ev, '['); + else + this->UnexpectedByteInMetaWarning(ev); + break; + + default: + if ( mParser_InTable && morkCh_IsHex(c) ) + this->ReadRow(ev, c); + else + this->UnexpectedByteInMetaWarning(ev); + break; + } + } + + // this->EndSpanOnThisByte(ev, &mParser_MetaSpan); + mParser_InMeta = morkBool_kFalse; + this->OnMetaEnd(ev, mParser_MetaSpan); +} + +/*static*/ void +morkParser::UnexpectedByteInMetaWarning(morkEnv* ev) +{ + ev->NewWarning("unexpected byte in meta"); +} + +/*static*/ void +morkParser::NonParserTypeError(morkEnv* ev) +{ + ev->NewError("non morkParser"); +} + +mork_bool morkParser::MatchPattern(morkEnv* ev, const char* inPattern) +{ + // if an error occurs, we want original inPattern in the debugger: + const char* pattern = inPattern; // mutable copy of pointer + morkStream* s = mParser_Stream; + int c; + while ( *pattern && ev->Good() ) + { + char byte = *pattern++; + if ( (c = s->Getc(ev)) != byte ) + { + ev->NewError("byte not in expected pattern"); + } + } + return ev->Good(); +} + +mork_bool morkParser::FindGroupEnd(morkEnv* ev) +{ + mork_bool foundEnd = morkBool_kFalse; + + // char gidBuf[ 64 ]; // to hold hex pattern we want + // (void) ev->TokenAsHex(gidBuf, mParser_GroupId); + + morkStream* s = mParser_Stream; + int c; + + while ( (c = s->Getc(ev)) != EOF && ev->Good() && !foundEnd ) + { + if ( c == '@' ) // maybe start of group ending? + { + // this->EndSpanOnThisByte(ev, &mParser_GroupSpan); + if ( (c = s->Getc(ev)) == '$' ) // '$' follows '@' ? + { + if ( (c = s->Getc(ev)) == '$' ) // '$' follows "@$" ? + { + if ( (c = s->Getc(ev)) == '}' ) + { + foundEnd = this->ReadEndGroupId(ev); + // this->EndSpanOnThisByte(ev, &mParser_GroupSpan); + + } + else + ev->NewError("expected '}' after @$$"); + } + } + if ( !foundEnd && c == '@' ) + s->Ungetc(c); + } + } + + return foundEnd && ev->Good(); +} + +void morkParser::ReadGroup(morkEnv* mev) +{ + nsIMdbEnv *ev = mev->AsMdbEnv(); + int next = 0; + mParser_GroupId = this->ReadHex(mev, &next); + if ( next == '{' ) + { + morkStream* s = mParser_Stream; + int c; + if ( (c = s->Getc(mev)) == '@' ) + { + // we really need the following span inside morkBuilder::OnNewGroup(): + this->StartSpanOnThisByte(mev, &mParser_GroupSpan); + mork_pos startPos = mParser_GroupSpan.mSpan_Start.mPlace_Pos; + + // if ( !store->mStore_FirstCommitGroupPos ) + // store->mStore_FirstCommitGroupPos = startPos; + // else if ( !store->mStore_SecondCommitGroupPos ) + // store->mStore_SecondCommitGroupPos = startPos; + + if ( this->FindGroupEnd(mev) ) + { + mork_pos outPos; + s->Seek(ev, startPos, &outPos); + if ( mev->Good() ) + { + this->OnNewGroup(mev, mParser_GroupSpan.mSpan_Start, + mParser_GroupId); + + this->ReadContent(mev, /*inInsideGroup*/ morkBool_kTrue); + + this->OnGroupCommitEnd(mev, mParser_GroupSpan); + } + } + } + else + mev->NewError("expected '@' after @$${id{"); + } + else + mev->NewError("expected '{' after @$$id"); + +} + +mork_bool morkParser::ReadAt(morkEnv* ev, mork_bool inInsideGroup) +/* groups must be ignored until properly terminated */ +// zm:Group ::= zm:GroupStart zm:Content zm:GroupEnd /* transaction */ +// zm:GroupStart ::= zm:S? '@$${' zm:Hex+ '{@' /* xaction id has own space */ +// zm:GroupEnd ::= zm:GroupCommit | zm:GroupAbort +// zm:GroupCommit ::= zm:S? '@$$}' zm:Hex+ '}@' /* id matches start id */ +// zm:GroupAbort ::= zm:S? '@$$}~~}@' /* id matches start id */ +/* We must allow started transactions to be aborted in summary files. */ +/* Note '$$' will never occur unescaped in values we will see in Mork. */ +{ + if ( this->MatchPattern(ev, "$$") ) + { + morkStream* s = mParser_Stream; + int c; + if ( ((c = s->Getc(ev)) == '{' || c == '}') && ev->Good() ) + { + if ( c == '{' ) // start of new group? + { + if ( !inInsideGroup ) + this->ReadGroup(ev); + else + ev->NewError("nested @$${ inside another group"); + } + else // c == '}' // end of old group? + { + if ( inInsideGroup ) + { + this->ReadEndGroupId(ev); + mParser_GroupId = 0; + } + else + ev->NewError("unmatched @$$} outside any group"); + } + } + else + ev->NewError("expected '{' or '}' after @$$"); + } + return ev->Good(); +} + +mork_bool morkParser::ReadEndGroupId(morkEnv* ev) +{ + mork_bool outSawGroupId = morkBool_kFalse; + morkStream* s = mParser_Stream; + int c; + if ( (c = s->Getc(ev)) != EOF && ev->Good() ) + { + if ( c == '~' ) // transaction is aborted? + { + this->MatchPattern(ev, "~}@"); // finish rest of pattern + } + else // push back byte and read expected trailing hex id + { + s->Ungetc(c); + int next = 0; + mork_gid endGroupId = this->ReadHex(ev, &next); + if ( ev->Good() ) + { + if ( endGroupId == mParser_GroupId ) // matches start? + { + if ( next == '}' ) // '}' after @$$}id ? + { + if ( (c = s->Getc(ev)) == '@' ) // '@' after @$$}id} ? + { + // looks good, so return with no error + outSawGroupId = morkBool_kTrue; + mParser_InGroup = false; + } + else + ev->NewError("expected '@' after @$$}id}"); + } + else + ev->NewError("expected '}' after @$$}id"); + } + else + ev->NewError("end group id mismatch"); + } + } + } + return ( outSawGroupId && ev->Good() ); +} + + +void morkParser::ReadDict(morkEnv* ev) +// zm:Dict ::= zm:S? '<' zm:DictItem* zm:S? '>' +// zm:DictItem ::= zm:MetaDict | zm:Alias +// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */ +// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')' +{ + mParser_Change = morkChange_kNil; + mParser_AtomChange = morkChange_kNil; + + // this->StartSpanOnLastByte(ev, &mParser_DictSpan); + mParser_InDict = morkBool_kTrue; + this->OnNewDict(ev, *mParser_DictSpan.AsPlace()); + + int c; + while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '>' ) + { + switch ( c ) + { + case '(': // alias + this->ReadAlias(ev); + break; + + case '<': // meta + this->ReadMeta(ev, '>'); + break; + + default: + ev->NewWarning("unexpected byte in dict"); + break; + } + } + + // this->EndSpanOnThisByte(ev, &mParser_DictSpan); + mParser_InDict = morkBool_kFalse; + this->OnDictEnd(ev, mParser_DictSpan); + + if ( ev->Bad() ) + mParser_State = morkParser_kBrokenState; + else if ( c == EOF ) + mParser_State = morkParser_kDoneState; +} + +void morkParser::EndSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) +{ + mork_pos here; + nsIMdbEnv *ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if (NS_SUCCEEDED(rv) && mev->Good() ) + { + this->SetHerePos(here); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::EndSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) +{ + mork_pos here; + nsIMdbEnv *ev = mev->AsMdbEnv(); + nsresult rv= mParser_Stream->Tell(ev, &here); + if ( NS_SUCCEEDED(rv) && mev->Good() ) + { + if ( here > 0 ) + --here; + else + here = 0; + + this->SetHerePos(here); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::StartSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan) +{ + mork_pos here; + nsIMdbEnv *ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if ( NS_SUCCEEDED(rv) && mev->Good() ) + { + if ( here > 0 ) + --here; + else + here = 0; + + this->SetHerePos(here); + ioSpan->SetStartWithEnd(mParser_PortSpan); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +void morkParser::StartSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan) +{ + mork_pos here; + nsIMdbEnv *ev = mev->AsMdbEnv(); + nsresult rv = mParser_Stream->Tell(ev, &here); + if ( NS_SUCCEEDED(rv) && mev->Good() ) + { + this->SetHerePos(here); + ioSpan->SetStartWithEnd(mParser_PortSpan); + ioSpan->SetEndWithEnd(mParser_PortSpan); + } +} + +mork_bool +morkParser::ReadContent(morkEnv* ev, mork_bool inInsideGroup) +{ + int c; + mork_bool keep_going = true; + while ( keep_going && (c = this->NextChar(ev)) != EOF && ev->Good()) + { + switch ( c ) + { + case '[': // row + this->ReadRow(ev, '['); + keep_going = false; + break; + + case '{': // table + this->ReadTable(ev); + keep_going = false; + break; + + case '<': // dict + this->ReadDict(ev); + keep_going = false; + break; + + case '@': // group + return this->ReadAt(ev, inInsideGroup); + // break; + + // case '+': // plus + // mParser_Change = morkChange_kAdd; + // break; + + // case '-': // minus + // mParser_Change = morkChange_kCut; + // break; + + // case '!': // bang + // mParser_Change = morkChange_kSet; + // break; + + default: + ev->NewWarning("unexpected byte in ReadContent()"); + break; + } + } + if ( ev->Bad() ) + mParser_State = morkParser_kBrokenState; + else if ( c == EOF ) + mParser_State = morkParser_kDoneState; + + return ( ev->Good() && c != EOF ); +} + +void +morkParser::OnPortState(morkEnv* ev) +{ + mork_bool firstTime = !mParser_InPort; + mParser_InPort = morkBool_kTrue; + if (firstTime) + this->OnNewPort(ev, *mParser_PortSpan.AsPlace()); + + mork_bool done = !this->ReadContent(ev, mParser_InGroup/*inInsideGroup*/); + + if (done) + { + mParser_InPort = morkBool_kFalse; + this->OnPortEnd(ev, mParser_PortSpan); + } + + if ( ev->Bad() ) + mParser_State = morkParser_kBrokenState; +} + +void +morkParser::OnStartState(morkEnv* mev) +{ + morkStream* s = mParser_Stream; + nsIMdbEnv *ev = mev->AsMdbEnv(); + if ( s && s->IsNode() && s->IsOpenNode() ) + { + mork_pos outPos; + nsresult rv = s->Seek(ev, 0, &outPos); + if (NS_SUCCEEDED(rv) && mev->Good() ) + { + this->StartParse(mev); + mParser_State = morkParser_kPortState; + } + } + else + mev->NilPointerError(); + + if ( mev->Bad() ) + mParser_State = morkParser_kBrokenState; +} + +/*protected non-poly*/ void +morkParser::ParseChunk(morkEnv* ev) +{ + mParser_Change = morkChange_kNil; + mParser_DoMore = morkBool_kTrue; + + switch ( mParser_State ) + { + case morkParser_kCellState: // 0 + this->OnCellState(ev); break; + + case morkParser_kMetaState: // 1 + this->OnMetaState(ev); break; + + case morkParser_kRowState: // 2 + this->OnRowState(ev); break; + + case morkParser_kTableState: // 3 + this->OnTableState(ev); break; + + case morkParser_kDictState: // 4 + this->OnDictState(ev); break; + + case morkParser_kPortState: // 5 + this->OnPortState(ev); break; + + case morkParser_kStartState: // 6 + this->OnStartState(ev); break; + + case morkParser_kDoneState: // 7 + mParser_DoMore = morkBool_kFalse; + mParser_IsDone = morkBool_kTrue; + this->StopParse(ev); + break; + case morkParser_kBrokenState: // 8 + mParser_DoMore = morkBool_kFalse; + mParser_IsBroken = morkBool_kTrue; + this->StopParse(ev); + break; + default: // ? + MORK_ASSERT(morkBool_kFalse); + mParser_State = morkParser_kBrokenState; + break; + } +} + +/*public non-poly*/ mdb_count +morkParser::ParseMore( // return count of bytes consumed now + morkEnv* ev, // context + mork_pos* outPos, // current byte pos in the stream afterwards + mork_bool* outDone, // is parsing finished? + mork_bool* outBroken // is parsing irreparably dead and broken? + ) +{ + mdb_count outCount = 0; + if ( this->IsNode() && this->GoodParserTag() && this->IsOpenNode() ) + { + mork_pos startPos = this->HerePos(); + + if ( !mParser_IsDone && !mParser_IsBroken ) + this->ParseChunk(ev); + + // HerePos is only updated for groups. I'd like it to be more accurate. + + mork_pos here; + mParser_Stream->Tell(ev, &here); + + if ( outDone ) + *outDone = mParser_IsDone; + if ( outBroken ) + *outBroken = mParser_IsBroken; + if ( outPos ) + *outPos = here; + + if ( here > startPos ) + outCount = (mdb_count) (here - startPos); + } + else + { + this->NonUsableParserError(ev); + if ( outDone ) + *outDone = morkBool_kTrue; + if ( outBroken ) + *outBroken = morkBool_kTrue; + if ( outPos ) + *outPos = 0; + } + return outCount; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkParser.h b/db/mork/src/morkParser.h new file mode 100644 index 0000000000..9ddfd7731d --- /dev/null +++ b/db/mork/src/morkParser.h @@ -0,0 +1,533 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKPARSER_ +#define _MORKPARSER_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKSINK_ +#include "morkSink.h" +#endif + +#ifndef _MORKYARN_ +#include "morkYarn.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkPlace: stream byte position and stream line count + */ + +class morkPlace { +public: + mork_pos mPlace_Pos; // byte offset in an input stream + mork_line mPlace_Line; // line count in an input stream + + void ClearPlace() + { + mPlace_Pos = 0; mPlace_Line = 0; + } + + void SetPlace(mork_pos inPos, mork_line inLine) + { + mPlace_Pos = inPos; mPlace_Line = inLine; + } + + morkPlace() { mPlace_Pos = 0; mPlace_Line = 0; } + + morkPlace(mork_pos inPos, mork_line inLine) + { mPlace_Pos = inPos; mPlace_Line = inLine; } + + morkPlace(const morkPlace& inPlace) + : mPlace_Pos(inPlace.mPlace_Pos), mPlace_Line(inPlace.mPlace_Line) { } +}; + +/*============================================================================= + * morkGlitch: stream place and error comment describing a parsing error + */ + +class morkGlitch { +public: + morkPlace mGlitch_Place; // place in stream where problem happened + const char* mGlitch_Comment; // null-terminated ASCII C string + + morkGlitch() { mGlitch_Comment = 0; } + + morkGlitch(const morkPlace& inPlace, const char* inComment) + : mGlitch_Place(inPlace), mGlitch_Comment(inComment) { } +}; + +/*============================================================================= + * morkMid: all possible ways needed to express an alias ID in Mork syntax + */ + +/*| morkMid: an abstraction of all the variations we might need to support +**| in order to present an ID through the parser interface most cheaply and +**| with minimum transformation away from the original text format. +**| +**|| An ID can have one of four forms: +**| 1) idHex (mMid_Oid.mOid_Id <- idHex) +**| 2) idHex:^scopeHex (mMid_Oid.mOid_Id <- idHex, mOid_Scope <- scopeHex) +**| 3) idHex:scopeName (mMid_Oid.mOid_Id <- idHex, mMid_Buf <- scopeName) +**| 4) columnName (mMid_Buf <- columnName, for columns in cells only) +**| +**|| Typically, mMid_Oid.mOid_Id will hold a nonzero integer value for +**| an ID, but we might have an optional scope specified by either an integer +**| in hex format, or a string name. (Note that while the first ID can be +**| scoped variably, any integer ID for a scope is assumed always located in +**| the same scope, so the second ID need not be disambiguated.) +**| +**|| The only time mMid_Oid.mOid_Id is ever zero is when mMid_Buf alone +**| is nonzero, to indicate an explicit string instead of an alias appeared. +**| This case happens to make the representation of columns in cells somewhat +**| easier to represent, since columns can just appear as a string name; and +**| this unifies those interfaces with row and table APIs expecting IDs. +**| +**|| So when the parser passes an instance of morkMid to a subclass, the +**| mMid_Oid.mOid_Id slot should usually be nonzero. And the other two +**| slots, mMid_Oid.mOid_Scope and mMid_Buf, might both be zero, or at +**| most one of them will be nonzero to indicate an explicit scope; the +**| parser is responsible for ensuring at most one of these is nonzero. +|*/ +class morkMid { +public: + mdbOid mMid_Oid; // mOid_Scope is zero when not specified + const morkBuf* mMid_Buf; // points to some specific buf subclass + + morkMid() + { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = 0; } + + void InitMidWithCoil(morkCoil* ioCoil) + { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = ioCoil; } + + void ClearMid() + { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne; + mMid_Buf = 0; } + + morkMid(const morkMid& other) + : mMid_Oid(other.mMid_Oid), mMid_Buf(other.mMid_Buf) { } + + mork_bool HasNoId() const // ID is unspecified? + { return ( mMid_Oid.mOid_Id == morkId_kMinusOne ); } + + mork_bool HasSomeId() const // ID is specified? + { return ( mMid_Oid.mOid_Id != morkId_kMinusOne ); } +}; + +/*============================================================================= + * morkSpan: start and end stream byte position and stream line count + */ + +class morkSpan { +public: + morkPlace mSpan_Start; + morkPlace mSpan_End; + +public: // methods + +public: // inlines + morkSpan() { } // use inline empty constructor for each place + + morkPlace* AsPlace() { return &mSpan_Start; } + const morkPlace* AsConstPlace() const { return &mSpan_Start; } + + void SetSpan(mork_pos inFromPos, mork_line inFromLine, + mork_pos inToPos, mork_line inToLine) + { + mSpan_Start.SetPlace(inFromPos, inFromLine); + mSpan_End.SetPlace(inToPos,inToLine); + } + + // setting end, useful to terminate a span using current port span end: + void SetEndWithEnd(const morkSpan& inSpan) // end <- span.end + { mSpan_End = inSpan.mSpan_End; } + + // setting start, useful to initiate a span using current port span end: + void SetStartWithEnd(const morkSpan& inSpan) // start <- span.end + { mSpan_Start = inSpan.mSpan_End; } + + void ClearSpan() + { + mSpan_Start.mPlace_Pos = 0; mSpan_Start.mPlace_Line = 0; + mSpan_End.mPlace_Pos = 0; mSpan_End.mPlace_Line = 0; + } + + morkSpan(mork_pos inFromPos, mork_line inFromLine, + mork_pos inToPos, mork_line inToLine) + : mSpan_Start(inFromPos, inFromLine), mSpan_End(inToPos, inToLine) + { /* empty implementation */ } +}; + +/*============================================================================= + * morkParser: for parsing Mork text syntax + */ + +#define morkParser_kMinGranularity 512 /* parse at least half 0.5K at once */ +#define morkParser_kMaxGranularity (64 * 1024) /* parse at most 64 K at once */ + +#define morkDerived_kParser /*i*/ 0x5073 /* ascii 'Ps' */ +#define morkParser_kTag /*i*/ 0x70417253 /* ascii 'pArS' */ + +// These are states for the simple parsing virtual machine. Needless to say, +// these must be distinct, and preferrably in a contiguous integer range. +// Don't change these constants without looking at switch statements in code. +#define morkParser_kCellState 0 /* cell is tightest scope */ +#define morkParser_kMetaState 1 /* meta is tightest scope */ +#define morkParser_kRowState 2 /* row is tightest scope */ +#define morkParser_kTableState 3 /* table is tightest scope */ +#define morkParser_kDictState 4 /* dict is tightest scope */ +#define morkParser_kPortState 5 /* port is tightest scope */ + +#define morkParser_kStartState 6 /* parsing has not yet begun */ +#define morkParser_kDoneState 7 /* parsing is complete */ +#define morkParser_kBrokenState 8 /* parsing is to broken to work */ + +class morkParser /*d*/ : public morkNode { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected morkParser members + + nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation + morkStream* mParser_Stream; // refcounted input stream + + mork_u4 mParser_Tag; // must equal morkParser_kTag + mork_count mParser_MoreGranularity; // constructor inBytesPerParseSegment + + mork_u4 mParser_State; // state where parser should resume + + // after finding ends of group transactions, we can re-seek the start: + mork_pos mParser_GroupContentStartPos; // start of this group + + morkMid mParser_TableMid; // table mid if inside a table + morkMid mParser_RowMid; // row mid if inside a row + morkMid mParser_CellMid; // cell mid if inside a row + mork_gid mParser_GroupId; // group ID if inside a group + + mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd? + mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd? + mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd? + mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd? + + mork_bool mParser_InPortRow; // called OnNewPortRow but not OnPortRowEnd? + mork_bool mParser_InRow; // called OnNewRow but not OnNewRowEnd? + mork_bool mParser_InTable; // called OnNewMeta but not OnMetaEnd? + mork_bool mParser_InGroup; // called OnNewGroup but not OnGroupEnd? + + mork_change mParser_AtomChange; // driven by mParser_Change + mork_change mParser_CellChange; // driven by mParser_Change + mork_change mParser_RowChange; // driven by mParser_Change + mork_change mParser_TableChange; // driven by mParser_Change + + mork_change mParser_Change; // driven by modifier in text + mork_bool mParser_IsBroken; // has the parse become broken? + mork_bool mParser_IsDone; // has the parse finished? + mork_bool mParser_DoMore; // mParser_MoreGranularity not exhausted? + + morkMid mParser_Mid; // current alias being parsed + // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below: + + // blob coils allocated in mParser_Heap + morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs + morkCoil mParser_ValueCoil; // place to accumulate value blobs + morkCoil mParser_ColumnCoil; // place to accumulate column blobs + morkCoil mParser_StringCoil; // place to accumulate string blobs + + morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil + morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil + morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil + morkSpool mParser_StringSpool; // writes to mParser_StringCoil + + // yarns allocated in mParser_Heap + morkYarn mParser_MidYarn; // place to receive from MidToYarn() + + // span showing current ongoing file position status: + morkSpan mParser_PortSpan; // span of current db port file + + // various spans denoting nested subspaces inside the file's port span: + morkSpan mParser_GroupSpan; // span of current transaction group + morkSpan mParser_DictSpan; + morkSpan mParser_AliasSpan; + morkSpan mParser_MetaSpan; + morkSpan mParser_TableSpan; + morkSpan mParser_RowSpan; + morkSpan mParser_CellSpan; + morkSpan mParser_ColumnSpan; + morkSpan mParser_SlotSpan; + +private: // convenience inlines + + mork_pos HerePos() const + { return mParser_PortSpan.mSpan_End.mPlace_Pos; } + + void SetHerePos(mork_pos inPos) + { mParser_PortSpan.mSpan_End.mPlace_Pos = inPos; } + + void CountLineBreak() + { ++mParser_PortSpan.mSpan_End.mPlace_Line; } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseParser() only if open + virtual ~morkParser(); // assert that CloseParser() executed earlier + +public: // morkYarn construction & destruction + morkParser(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStream* ioStream, // the readonly stream for input bytes + mdb_count inBytesPerParseSegment, // target for ParseMore() + nsIMdbHeap* ioSlotHeap); + + void CloseParser(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkParser(const morkParser& other); + morkParser& operator=(const morkParser& other); + +public: // dynamic type identification + mork_bool IsParser() const + { return IsNode() && mNode_Derived == morkDerived_kParser; } + +// } ===== end morkNode methods ===== + +public: // errors and warnings + static void UnexpectedEofError(morkEnv* ev); + static void EofInsteadOfHexError(morkEnv* ev); + static void ExpectedEqualError(morkEnv* ev); + static void ExpectedHexDigitError(morkEnv* ev, int c); + static void NonParserTypeError(morkEnv* ev); + static void UnexpectedByteInMetaWarning(morkEnv* ev); + +public: // other type methods + mork_bool GoodParserTag() const { return mParser_Tag == morkParser_kTag; } + void NonGoodParserError(morkEnv* ev); + void NonUsableParserError(morkEnv* ev); + // call when IsNode() or GoodParserTag() is false + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // in virtual morkParser methods, data flow subclass to parser + + virtual void MidToYarn(morkEnv* ev, + const morkMid& inMid, // typically an alias to concat with strings + mdbYarn* outYarn) = 0; + // The parser might ask that some aliases be turned into yarns, so they + // can be concatenated into longer blobs under some circumstances. This + // is an alternative to using a long and complex callback for many parts + // for a single cell value. + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // out virtual morkParser methods, data flow parser to subclass + +// The virtual methods below will be called in a pattern corresponding +// to the following grammar isomorphic to the Mork grammar. There should +// be no exceptions, so subclasses can rely on seeing an appropriate "end" +// method whenever some "new" method has been seen earlier. In the event +// that some error occurs that causes content to be flushed, or sudden early +// termination of a larger containing entity, we will always call a more +// enclosed "end" method before we call an "end" method with greater scope. + +// Note the "mp" prefix stands for "Mork Parser": + +// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd +// mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch +// mp:Group ::= OnNewGroup mp:GroupItem* mp:GroupEnd +// mp:GroupItem ::= mp:Content | OnGroupGlitch +// mp:GroupEnd ::= OnGroupCommitEnd | OnGroupAbortEnd +// mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row +// mp:PortRow ::= OnNewPortRow mp:RowItem* OnPortRowEnd +// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd +// mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch +// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd +// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch +// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd +// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd +// mp:MetaItem ::= mp:Cell | OnMetaGlitch +// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd +// mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch +// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd +// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch +// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid + + + // Note that in interfaces below, mork_change parameters kAdd and kNil + // both mean about the same thing by default. Only kCut is interesting, + // because this usually means to remove members instead of adding them. + + virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) = 0; + virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_change inChange) = 0; + virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllRows) = 0; + virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnMinusRow(morkEnv* ev) = 0; + virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace, + const morkMid& inMid, mork_bool inCutAllCols) = 0; + virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) = 0; + virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) = 0; + virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + + virtual void OnMinusCell(morkEnv* ev) = 0; + virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace, + const morkMid* inMid, const morkBuf* inBuf) = 0; + // Exactly one of inMid and inBuf is nil, and the other is non-nil. + // When hex ID syntax is used for a column, then inMid is not nil, and + // when a naked string names a column, then inBuf is not nil. + + virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0; + virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) = 0; + virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) = 0; + + virtual void OnValue(morkEnv* ev, const morkSpan& inSpan, + const morkBuf& inBuf) = 0; + + virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + + virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan, + const morkMid& inMid) = 0; + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected parser helper methods + + void ParseChunk(morkEnv* ev); // find parse continuation and resume + + void StartParse(morkEnv* ev); // prepare for parsing + void StopParse(morkEnv* ev); // terminate parsing & call needed methods + + int NextChar(morkEnv* ev); // next non-white content + + void OnCellState(morkEnv* ev); + void OnMetaState(morkEnv* ev); + void OnRowState(morkEnv* ev); + void OnTableState(morkEnv* ev); + void OnDictState(morkEnv* ev); + void OnPortState(morkEnv* ev); + void OnStartState(morkEnv* ev); + + void ReadCell(morkEnv* ev); + void ReadRow(morkEnv* ev, int c); + void ReadRowPos(morkEnv* ev); + void ReadTable(morkEnv* ev); + void ReadTableMeta(morkEnv* ev); + void ReadDict(morkEnv* ev); + mork_bool ReadContent(morkEnv* ev, mork_bool inInsideGroup); + void ReadGroup(morkEnv* ev); + mork_bool ReadEndGroupId(morkEnv* ev); + mork_bool ReadAt(morkEnv* ev, mork_bool inInsideGroup); + mork_bool FindGroupEnd(morkEnv* ev); + void ReadMeta(morkEnv* ev, int inEndMeta); + void ReadAlias(morkEnv* ev); + mork_id ReadHex(morkEnv* ev, int* outNextChar); + morkBuf* ReadValue(morkEnv* ev); + morkBuf* ReadName(morkEnv* ev, int c); + mork_bool ReadMid(morkEnv* ev, morkMid* outMid); + void ReadDictForm(morkEnv *ev); + void ReadCellForm(morkEnv *ev, int c); + + mork_bool MatchPattern(morkEnv* ev, const char* inPattern); + + void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan); + void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan); + void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan); + + void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan); + + + // void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + // void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan) + // { MORK_USED_2(ev,ioSpan); } + + int eat_line_break(morkEnv* ev, int inLast); + int eat_line_continue(morkEnv* ev); // last char was '\\' + int eat_comment(morkEnv* ev); // last char was '/' + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkParser methods + + mdb_count ParseMore( // return count of bytes consumed now + morkEnv* ev, // context + mork_pos* outPos, // current byte pos in the stream afterwards + mork_bool* outDone, // is parsing finished? + mork_bool* outBroken // is parsing irreparably dead and broken? + ); + + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakParser(morkParser* me, + morkEnv* ev, morkParser** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongParser(morkParser* me, + morkEnv* ev, morkParser** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPARSER_ */ + diff --git a/db/mork/src/morkPool.cpp b/db/mork/src/morkPool.cpp new file mode 100644 index 0000000000..6819b8bd05 --- /dev/null +++ b/db/mork/src/morkPool.cpp @@ -0,0 +1,552 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKHANDLE_ +#include "morkHandle.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +#ifndef _MORKZONE_ +#include "morkZone.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkPool::CloseMorkNode(morkEnv* ev) // ClosePool() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->ClosePool(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPool::~morkPool() // assert ClosePool() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkPool::morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap) +: morkNode(inUsage, ioHeap) +, mPool_Heap( ioSlotHeap ) +, mPool_UsedFramesCount( 0 ) +, mPool_FreeFramesCount( 0 ) +{ + // mPool_Heap is NOT refcounted + MORK_ASSERT(ioSlotHeap); + if ( ioSlotHeap ) + mNode_Derived = morkDerived_kPool; +} + +/*public non-poly*/ +morkPool::morkPool(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkNode(ev, inUsage, ioHeap) +, mPool_Heap( ioSlotHeap ) +, mPool_UsedFramesCount( 0 ) +, mPool_FreeFramesCount( 0 ) +{ + if ( ioSlotHeap ) + { + // mPool_Heap is NOT refcounted: + // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mPool_Heap); + if ( ev->Good() ) + mNode_Derived = morkDerived_kPool; + } + else + ev->NilPointerError(); +} + +/*public non-poly*/ void +morkPool::ClosePool(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ + //MORK_USED_1(ioZone); +#endif /*morkZone_CONFIG_ARENA*/ + + nsIMdbHeap* heap = mPool_Heap; + nsIMdbEnv* mev = ev->AsMdbEnv(); + morkLink* aLink; + morkDeque* d = &mPool_FreeHandleFrames; + while ( (aLink = d->RemoveFirst()) != 0 ) + heap->Free(mev, aLink); + + // if the pool's closed, get rid of the frames in use too. + d = &mPool_UsedHandleFrames; + while ( (aLink = d->RemoveFirst()) != 0 ) + heap->Free(mev, aLink); + + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + + +// alloc and free individual instances of handles (inside hand frames): +morkHandleFace* +morkPool::NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone) +{ + void* newBlock = 0; + if ( inSize <= sizeof(morkHandleFrame) ) + { + morkLink* firstLink = mPool_FreeHandleFrames.RemoveFirst(); + if ( firstLink ) + { + newBlock = firstLink; + if ( mPool_FreeFramesCount ) + --mPool_FreeFramesCount; + else + ev->NewWarning("mPool_FreeFramesCount underflow"); + } + else + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkHandleFrame), + (void**) &newBlock); + } + else + { + ev->NewWarning("inSize > sizeof(morkHandleFrame)"); + mPool_Heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &newBlock); + } +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); +#endif /*morkZone_CONFIG_ARENA*/ + + return (morkHandleFace*) newBlock; +} + +void +morkPool::ZapHandle(morkEnv* ev, morkHandleFace* ioHandle) +{ + if ( ioHandle ) + { + morkLink* handleLink = (morkLink*) ioHandle; + mPool_FreeHandleFrames.AddLast(handleLink); + ++mPool_FreeFramesCount; + // lets free all handles to track down leaks + // - uncomment out next 3 lines, comment out above 2 +// nsIMdbHeap* heap = mPool_Heap; +// nsIMdbEnv* mev = ev->AsMdbEnv(); +// heap->Free(mev, handleLink); + + } +} + + +// alloc and free individual instances of rows: +morkRow* +morkPool::NewRow(morkEnv* ev, morkZone* ioZone) // allocate a new row instance +{ + morkRow* newRow = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newRow = (morkRow*) ioZone->ZoneNewChip(ev, sizeof(morkRow)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkRow), (void**) &newRow); +#endif /*morkZone_CONFIG_ARENA*/ + + if ( newRow ) + MORK_MEMSET(newRow, 0, sizeof(morkRow)); + + return newRow; +} + +void +morkPool::ZapRow(morkEnv* ev, morkRow* ioRow, + morkZone* ioZone) // free old row instance +{ +#ifdef morkZone_CONFIG_ARENA + if ( !ioRow ) + ev->NilPointerWarning(); // a zone 'chip' cannot be freed +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + if ( ioRow ) + mPool_Heap->Free(ev->AsMdbEnv(), ioRow); +#endif /*morkZone_CONFIG_ARENA*/ +} + +// alloc and free entire vectors of cells (not just one cell at a time) +morkCell* +morkPool::NewCells(morkEnv* ev, mork_size inSize, + morkZone* ioZone) +{ + morkCell* newCells = 0; + + mork_size size = inSize * sizeof(morkCell); + if ( size ) + { +#ifdef morkZone_CONFIG_ARENA + // a zone 'run' knows its size, and can indeed be deallocated: + newCells = (morkCell*) ioZone->ZoneNewRun(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newCells); +#endif /*morkZone_CONFIG_ARENA*/ + } + + // note morkAtom depends on having nil stored in all new mCell_Atom slots: + if ( newCells ) + MORK_MEMSET(newCells, 0, size); + return newCells; +} + +void +morkPool::ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, + morkZone* ioZone) +{ + MORK_USED_1(inSize); + + if ( ioVector ) + { +#ifdef morkZone_CONFIG_ARENA + // a zone 'run' knows its size, and can indeed be deallocated: + ioZone->ZoneZapRun(ev, ioVector); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Free(ev->AsMdbEnv(), ioVector); +#endif /*morkZone_CONFIG_ARENA*/ + } +} + +// resize (grow or trim) cell vectors inside a containing row instance +mork_bool +morkPool::AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, + morkZone* ioZone) +{ + // note strong implementation similarity to morkArray::Grow() + + MORK_USED_1(ioZone); +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ +#endif /*morkZone_CONFIG_ARENA*/ + + mork_fill fill = ioRow->mRow_Length; + if ( ev->Good() && fill < inNewSize ) // need more cells? + { + morkCell* newCells = this->NewCells(ev, inNewSize, ioZone); + if ( newCells ) + { + morkCell* c = newCells; // for iterating during copy + morkCell* oldCells = ioRow->mRow_Cells; + morkCell* end = oldCells + fill; // copy all the old cells + while ( oldCells < end ) + { + *c++ = *oldCells++; // bitwise copy each old cell struct + } + oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = newCells; + ioRow->mRow_Length = (mork_u2) inNewSize; + ++ioRow->mRow_Seed; + + if ( oldCells ) + this->ZapCells(ev, oldCells, fill, ioZone); + } + } + return ( ev->Good() && ioRow->mRow_Length >= inNewSize ); +} + +mork_bool +morkPool::CutRowCells(morkEnv* ev, morkRow* ioRow, + mork_size inNewSize, + morkZone* ioZone) +{ + MORK_USED_1(ioZone); +#ifdef morkZone_CONFIG_ARENA +#else /*morkZone_CONFIG_ARENA*/ +#endif /*morkZone_CONFIG_ARENA*/ + + mork_fill fill = ioRow->mRow_Length; + if ( ev->Good() && fill > inNewSize ) // need fewer cells? + { + if ( inNewSize ) // want any row cells at all? + { + morkCell* newCells = this->NewCells(ev, inNewSize, ioZone); + if ( newCells ) + { + morkCell* saveNewCells = newCells; // Keep newcell pos + morkCell* oldCells = ioRow->mRow_Cells; + morkCell* oldEnd = oldCells + fill; // one past all old cells + morkCell* newEnd = oldCells + inNewSize; // copy only kept old cells + while ( oldCells < newEnd ) + { + *newCells++ = *oldCells++; // bitwise copy each old cell struct + } + while ( oldCells < oldEnd ) + { + if ( oldCells->mCell_Atom ) // need to unref old cell atom? + oldCells->SetAtom(ev, (morkAtom*) 0, this); // unref cell atom + ++oldCells; + } + oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = saveNewCells; + ioRow->mRow_Length = (mork_u2) inNewSize; + ++ioRow->mRow_Seed; + + if ( oldCells ) + this->ZapCells(ev, oldCells, fill, ioZone); + } + } + else // get rid of all row cells + { + morkCell* oldCells = ioRow->mRow_Cells; + ioRow->mRow_Cells = 0; + ioRow->mRow_Length = 0; + ++ioRow->mRow_Seed; + + if ( oldCells ) + this->ZapCells(ev, oldCells, fill, ioZone); + } + } + return ( ev->Good() && ioRow->mRow_Length <= inNewSize ); +} + +// alloc & free individual instances of atoms (lots of atom subclasses): +void +morkPool::ZapAtom(morkEnv* ev, morkAtom* ioAtom, + morkZone* ioZone) // any subclass (by kind) +{ +#ifdef morkZone_CONFIG_ARENA + if ( !ioAtom ) + ev->NilPointerWarning(); // a zone 'chip' cannot be freed +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + if ( ioAtom ) + mPool_Heap->Free(ev->AsMdbEnv(), ioAtom); +#endif /*morkZone_CONFIG_ARENA*/ +} + +morkOidAtom* +morkPool::NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone) +{ + morkOidAtom* newAtom = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom),(void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + + if ( newAtom ) + newAtom->InitRowOidAtom(ev, inOid); + return newAtom; +} + +morkOidAtom* +morkPool::NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, + morkZone* ioZone) +{ + morkOidAtom* newAtom = 0; + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom)); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom), (void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if ( newAtom ) + newAtom->InitTableOidAtom(ev, inOid); + return newAtom; +} + +morkAtom* +morkPool::NewAnonAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, + morkZone* ioZone) +// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' +// anon atom will be created, and otherwise a 'big' anon atom. +{ + morkAtom* newAtom = 0; + + mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 ); + mork_size size = ( needBig )? + morkBigAnonAtom::SizeForFill(inBuf.mBuf_Fill) : + morkWeeAnonAtom::SizeForFill(inBuf.mBuf_Fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkAtom*) ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if ( newAtom ) + { + if ( needBig ) + ((morkBigAnonAtom*) newAtom)->InitBigAnonAtom(ev, inBuf, inForm); + else + ((morkWeeAnonAtom*) newAtom)->InitWeeAnonAtom(ev, inBuf); + } + return newAtom; +} + +morkBookAtom* +morkPool::NewBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid, + morkZone* ioZone) +// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' +// book atom will be created, and otherwise a 'big' book atom. +{ + morkBookAtom* newAtom = 0; + + mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 ); + mork_size size = ( needBig )? + morkBigBookAtom::SizeForFill(inBuf.mBuf_Fill) : + morkWeeBookAtom::SizeForFill(inBuf.mBuf_Fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if ( newAtom ) + { + if ( needBig ) + ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev, + inBuf, inForm, ioSpace, inAid); + else + ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev, + inBuf, ioSpace, inAid); + } + return newAtom; +} + +morkBookAtom* +morkPool::NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom, + morkZone* ioZone) + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. +{ + morkBookAtom* newAtom = 0; + + mork_cscode form = inAtom.mBigBookAtom_Form; + mork_fill fill = inAtom.mBigBookAtom_Size; + mork_bool needBig = ( form || fill > 255 ); + mork_size size = ( needBig )? + morkBigBookAtom::SizeForFill(fill) : + morkWeeBookAtom::SizeForFill(fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if ( newAtom ) + { + morkBuf buf(inAtom.mBigBookAtom_Body, fill); + if ( needBig ) + ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev, + buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id); + else + ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev, + buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id); + } + return newAtom; +} + +morkBookAtom* +morkPool::NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom, + morkZone* ioZone) + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. +{ + morkBookAtom* newAtom = 0; + + mork_cscode form = inAtom.mFarBookAtom_Form; + mork_fill fill = inAtom.mFarBookAtom_Size; + mork_bool needBig = ( form || fill > 255 ); + mork_size size = ( needBig )? + morkBigBookAtom::SizeForFill(fill) : + morkWeeBookAtom::SizeForFill(fill); + +#ifdef morkZone_CONFIG_ARENA + // a zone 'chip' remembers no size, and so cannot be deallocated: + newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size); +#else /*morkZone_CONFIG_ARENA*/ + MORK_USED_1(ioZone); + mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom); +#endif /*morkZone_CONFIG_ARENA*/ + if ( newAtom ) + { + morkBuf buf(inAtom.mFarBookAtom_Body, fill); + if ( needBig ) + ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev, + buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id); + else + ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev, + buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id); + } + return newAtom; +} + + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkPool.h b/db/mork/src/morkPool.h new file mode 100644 index 0000000000..4cf9a4e045 --- /dev/null +++ b/db/mork/src/morkPool.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKPOOL_ +#define _MORKPOOL_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkHandle; +class morkHandleFrame; +class morkHandleFace; // just an opaque cookie type +class morkBigBookAtom; +class morkFarBookAtom; + +#define morkDerived_kPool /*i*/ 0x706C /* ascii 'pl' */ + +/*| morkPool: a place to manage pools of non-node objects that are memory +**| managed out of large chunks of space, so that per-object management +**| space overhead has no signficant cost. +|*/ +class morkPool : public morkNode { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + nsIMdbHeap* mPool_Heap; // NON-refcounted heap instance + + morkDeque mPool_Blocks; // linked list of large blocks from heap + + // These two lists contain instances of morkHandleFrame: + morkDeque mPool_UsedHandleFrames; // handle frames currently being used + morkDeque mPool_FreeHandleFrames; // handle frames currently in free list + + mork_count mPool_UsedFramesCount; // length of mPool_UsedHandleFrames + mork_count mPool_FreeFramesCount; // length of mPool_UsedHandleFrames + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev); // ClosePool() only if open + virtual ~morkPool(); // assert that ClosePool() executed earlier + +public: // morkPool construction & destruction + morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + morkPool(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap); + void ClosePool(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkPool(const morkPool& other); + morkPool& operator=(const morkPool& other); + +public: // dynamic type identification + mork_bool IsPool() const + { return IsNode() && mNode_Derived == morkDerived_kPool; } +// } ===== end morkNode methods ===== + +public: // typing + void NonPoolTypeError(morkEnv* ev); + +public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkNode::MakeNew(inSize, ioHeap, ev); } + + void* operator new(size_t inSize) CPP_THROW_NEW + { return ::operator new(inSize); } + + +public: // other pool methods + + // alloc and free individual instances of handles (inside hand frames): + morkHandleFace* NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone); + void ZapHandle(morkEnv* ev, morkHandleFace* ioHandle); + + // alloc and free individual instances of rows: + morkRow* NewRow(morkEnv* ev, morkZone* ioZone); // alloc new row instance + void ZapRow(morkEnv* ev, morkRow* ioRow, morkZone* ioZone); // free old row instance + + // alloc and free entire vectors of cells (not just one cell at a time) + morkCell* NewCells(morkEnv* ev, mork_size inSize, morkZone* ioZone); + void ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, morkZone* ioZone); + + // resize (grow or trim) cell vectors inside a containing row instance + mork_bool AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone); + mork_bool CutRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone); + + // alloc & free individual instances of atoms (lots of atom subclasses): + void ZapAtom(morkEnv* ev, morkAtom* ioAtom, morkZone* ioZone); // any subclass (by kind) + + morkOidAtom* NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone); + morkOidAtom* NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone); + + morkAtom* NewAnonAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkZone* ioZone); + // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' + // anon atom will be created, and otherwise a 'big' anon atom. + + morkBookAtom* NewBookAtom(morkEnv* ev, const morkBuf& inBuf, + mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid, morkZone* ioZone); + // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee' + // book atom will be created, and otherwise a 'big' book atom. + + morkBookAtom* NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom, morkZone* ioZone); + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. + + morkBookAtom* NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom, morkZone* ioZone); + // make the smallest kind of book atom that can hold content in inAtom. + // The inAtom parameter is often expected to be a staged book atom in + // the store, which was used to search an atom space for existing atoms. + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakPool(morkPool* me, + morkEnv* ev, morkPool** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongPool(morkPool* me, + morkEnv* ev, morkPool** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPOOL_ */ + diff --git a/db/mork/src/morkPortTableCursor.cpp b/db/mork/src/morkPortTableCursor.cpp new file mode 100644 index 0000000000..2542fcc49f --- /dev/null +++ b/db/mork/src/morkPortTableCursor.cpp @@ -0,0 +1,430 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKPORTTABLECURSOR_ +#include "morkPortTableCursor.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkPortTableCursor::CloseMorkNode(morkEnv* ev) // ClosePortTableCursor() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->ClosePortTableCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkPortTableCursor::~morkPortTableCursor() // ClosePortTableCursor() executed earlier +{ + CloseMorkNode(mMorkEnv); +} + +/*public non-poly*/ +morkPortTableCursor::morkPortTableCursor(morkEnv* ev, + const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope, + mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap) +: morkCursor(ev, inUsage, ioHeap) +, mPortTableCursor_Store( 0 ) +, mPortTableCursor_RowScope( (mdb_scope) -1 ) // we want != inRowScope +, mPortTableCursor_TableKind( (mdb_kind) -1 ) // we want != inTableKind +, mPortTableCursor_LastTable ( 0 ) // not refcounted +, mPortTableCursor_RowSpace( 0 ) // strong ref to row space +, mPortTableCursor_TablesDidEnd( morkBool_kFalse ) +, mPortTableCursor_SpacesDidEnd( morkBool_kFalse ) +{ + if ( ev->Good() ) + { + if ( ioStore && ioSlotHeap ) + { + mCursor_Pos = -1; + mCursor_Seed = 0; // let the iterator do its own seed handling + morkStore::SlotWeakStore(ioStore, ev, &mPortTableCursor_Store); + + if ( this->SetRowScope(ev, inRowScope) ) + this->SetTableKind(ev, inTableKind); + + if ( ev->Good() ) + mNode_Derived = morkDerived_kPortTableCursor; + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkPortTableCursor, morkCursor, nsIMdbPortTableCursor) + +morkEnv* +morkPortTableCursor::CanUsePortTableCursor(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const +{ + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( IsPortTableCursor() ) + outEnv = ev; + else + NonPortTableCursorTypeError(ev); + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv); + return outEnv; +} + + +/*public non-poly*/ void +morkPortTableCursor::ClosePortTableCursor(morkEnv* ev) +{ + if ( this->IsNode() ) + { + mCursor_Pos = -1; + mCursor_Seed = 0; + mPortTableCursor_LastTable = 0; + morkStore::SlotWeakStore((morkStore*) 0, ev, &mPortTableCursor_Store); + morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev, + &mPortTableCursor_RowSpace); + this->CloseCursor(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkPortTableCursor::NilCursorStoreError(morkEnv* ev) +{ + ev->NewError("nil mPortTableCursor_Store"); +} + +/*static*/ void +morkPortTableCursor::NonPortTableCursorTypeError(morkEnv* ev) +{ + ev->NewError("non morkPortTableCursor"); +} + +mork_bool +morkPortTableCursor::SetRowScope(morkEnv* ev, mork_scope inRowScope) +{ + mPortTableCursor_RowScope = inRowScope; + mPortTableCursor_LastTable = 0; // restart iteration of space + + mPortTableCursor_TableIter.CloseMapIter(ev); + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + mPortTableCursor_SpacesDidEnd = morkBool_kTrue; + + morkStore* store = mPortTableCursor_Store; + if ( store ) + { + morkRowSpace* space = mPortTableCursor_RowSpace; + + if ( inRowScope ) // intend to cover a specific scope only? + { + space = store->LazyGetRowSpace(ev, inRowScope); + morkRowSpace::SlotStrongRowSpace(space, ev, + &mPortTableCursor_RowSpace); + + // We want mPortTableCursor_SpacesDidEnd == morkBool_kTrue + // to show this is the only space to be covered. + } + else // prepare space map iter to cover all space scopes + { + morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + space = 0; + (void) rsi->FirstRowSpace(ev, (mork_scope*) 0, &space); + morkRowSpace::SlotStrongRowSpace(space, ev, + &mPortTableCursor_RowSpace); + + if ( space ) // found first space in store + mPortTableCursor_SpacesDidEnd = morkBool_kFalse; + } + + this->init_space_tables_map(ev); + } + else + this->NilCursorStoreError(ev); + + return ev->Good(); +} + +void +morkPortTableCursor::init_space_tables_map(morkEnv* ev) +{ + morkRowSpace* space = mPortTableCursor_RowSpace; + if ( space && ev->Good() ) + { + morkTableMapIter* ti = &mPortTableCursor_TableIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + if ( ev->Good() ) + mPortTableCursor_TablesDidEnd = morkBool_kFalse; + } +} + + +mork_bool +morkPortTableCursor::SetTableKind(morkEnv* ev, mork_kind inTableKind) +{ + mPortTableCursor_TableKind = inTableKind; + mPortTableCursor_LastTable = 0; // restart iteration of space + + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + + morkRowSpace* space = mPortTableCursor_RowSpace; + if ( !space && mPortTableCursor_RowScope == 0 ) + { + this->SetRowScope(ev, 0); + } + this->init_space_tables_map(ev); + + return ev->Good(); +} + +morkRowSpace* +morkPortTableCursor::NextSpace(morkEnv* ev) +{ + morkRowSpace* outSpace = 0; + mPortTableCursor_LastTable = 0; + mPortTableCursor_SpacesDidEnd = morkBool_kTrue; + mPortTableCursor_TablesDidEnd = morkBool_kTrue; + + if ( !mPortTableCursor_RowScope ) // not just one scope? + { + morkStore* store = mPortTableCursor_Store; + if ( store ) + { + morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter; + + (void) rsi->NextRowSpace(ev, (mork_scope*) 0, &outSpace); + morkRowSpace::SlotStrongRowSpace(outSpace, ev, + &mPortTableCursor_RowSpace); + + if ( outSpace ) // found next space in store + { + mPortTableCursor_SpacesDidEnd = morkBool_kFalse; + + this->init_space_tables_map(ev); + + if ( ev->Bad() ) + outSpace = 0; + } + } + else + this->NilCursorStoreError(ev); + } + + return outSpace; +} + +morkTable * +morkPortTableCursor::NextTable(morkEnv* ev) +{ + mork_kind kind = mPortTableCursor_TableKind; + + do // until spaces end, or until we find a table in a space + { + morkRowSpace* space = mPortTableCursor_RowSpace; + if ( mPortTableCursor_TablesDidEnd ) // current space exhausted? + space = this->NextSpace(ev); // go on to the next space + + if ( space ) // have a space remaining that might hold tables? + { +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTableMapIter* ti = &mPortTableCursor_TableIter; + morkTable* table = ( mPortTableCursor_LastTable )? + ti->NextTable(ev) : ti->FirstTable(ev); + + for ( ; table && ev->Good(); table = ti->NextTable(ev) ) + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key = 0; // ignore keys in table map + morkTable* table = 0; // old value table in the map + morkTableMapIter* ti = &mPortTableCursor_TableIter; + mork_change* c = ( mPortTableCursor_LastTable )? + ti->NextTable(ev, key, &table) : ti->FirstTable(ev, key, &table); + + for ( ; c && ev->Good(); c = ti->NextTable(ev, key, &table) ) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if ( table && table->IsTable() ) + { + if ( !kind || kind == table->mTable_Kind ) + { + mPortTableCursor_LastTable = table; // ti->NextTable() hence + return table; + } + } + else + table->NonTableTypeWarning(ev); + } + mPortTableCursor_TablesDidEnd = morkBool_kTrue; // space is done + mPortTableCursor_LastTable = 0; // make sure next space starts fresh + } + + } while ( ev->Good() && !mPortTableCursor_SpacesDidEnd ); + + return (morkTable*) 0; +} + + +// { ----- begin table iteration methods ----- + +// { ===== begin nsIMdbPortTableCursor methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkPortTableCursor::SetPort(nsIMdbEnv* mev, nsIMdbPort* ioPort) +{ + NS_ASSERTION(false,"not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkPortTableCursor::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) +{ + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + if ( mPortTableCursor_Store ) + outPort = mPortTableCursor_Store->AcquireStoreHandle(ev); + outErr = ev->AsErr(); + } + if ( acqPort ) + *acqPort = outPort; + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::SetRowScope(nsIMdbEnv* mev, // sets pos to -1 + mdb_scope inRowScope) +{ + nsresult outErr = NS_OK; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + mCursor_Pos = -1; + + SetRowScope(ev, inRowScope); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) +{ + nsresult outErr = NS_OK; + mdb_scope rowScope = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + rowScope = mPortTableCursor_RowScope; + outErr = ev->AsErr(); + } + *outRowScope = rowScope; + return outErr; +} +// setting row scope to zero iterates over all row scopes in port + +NS_IMETHODIMP +morkPortTableCursor::SetTableKind(nsIMdbEnv* mev, // sets pos to -1 + mdb_kind inTableKind) +{ + nsresult outErr = NS_OK; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + mCursor_Pos = -1; + + SetTableKind(ev, inTableKind); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkPortTableCursor::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) +// setting table kind to zero iterates over all table kinds in row scope +{ + nsresult outErr = NS_OK; + mdb_kind tableKind = 0; + morkEnv* ev = + this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + tableKind = mPortTableCursor_TableKind; + outErr = ev->AsErr(); + } + *outTableKind = tableKind; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin table iteration methods ----- +NS_IMETHODIMP +morkPortTableCursor::NextTable( // get table at next position in the db + nsIMdbEnv* mev, // context + nsIMdbTable** acqTable) +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = + CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = NextTable(ev); + if ( table && ev->Good() ) + outTable = table->AcquireTableHandle(ev); + + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkPortTableCursor.h b/db/mork/src/morkPortTableCursor.h new file mode 100644 index 0000000000..cc9edaa679 --- /dev/null +++ b/db/mork/src/morkPortTableCursor.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKPORTTABLECURSOR_ +#define _MORKPORTTABLECURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinPortTableCursor; +#define morkDerived_kPortTableCursor /*i*/ 0x7443 /* ascii 'tC' */ + +class morkPortTableCursor : public morkCursor, public nsIMdbPortTableCursor { // row iterator +public: + NS_DECL_ISUPPORTS_INHERITED +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + +public: // state is public because the entire Mork system is private + // { ----- begin attribute methods ----- + NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) override; // sets pos to -1 + NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) override; + + NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1 + mdb_scope inRowScope) override; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override; + // setting row scope to zero iterates over all row scopes in port + + NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1 + mdb_kind inTableKind) override; + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override; + // setting table kind to zero iterates over all table kinds in row scope + // } ----- end attribute methods ----- + + // { ----- begin table iteration methods ----- + NS_IMETHOD NextTable( // get table at next position in the db + nsIMdbEnv* ev, // context + nsIMdbTable** acqTable) override; // the next table in the iteration + // } ----- end table iteration methods ----- + morkStore* mPortTableCursor_Store; // weak ref to store + + mdb_scope mPortTableCursor_RowScope; + mdb_kind mPortTableCursor_TableKind; + + // We only care if LastTable is non-nil, so it is not refcounted; + // so you must never access table state or methods using LastTable: + + morkTable* mPortTableCursor_LastTable; // nil or last table (no refcount) + morkRowSpace* mPortTableCursor_RowSpace; // current space (strong ref) + + morkRowSpaceMapIter mPortTableCursor_SpaceIter; // iter over spaces + morkTableMapIter mPortTableCursor_TableIter; // iter over tables + + // these booleans indicate when the table or space iterator is exhausted: + + mork_bool mPortTableCursor_TablesDidEnd; // no more tables? + mork_bool mPortTableCursor_SpacesDidEnd; // no more spaces? + mork_u1 mPortTableCursor_Pad[ 2 ]; // for u4 alignment + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // ClosePortTableCursor() + +public: // morkPortTableCursor construction & destruction + morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope, + mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap); + void ClosePortTableCursor(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkPortTableCursor(const morkPortTableCursor& other); + morkPortTableCursor& operator=(const morkPortTableCursor& other); + +public: // dynamic type identification + mork_bool IsPortTableCursor() const + { return IsNode() && mNode_Derived == morkDerived_kPortTableCursor; } +// } ===== end morkNode methods ===== + +protected: // utilities + virtual ~morkPortTableCursor(); // assert that close executed earlier + + void init_space_tables_map(morkEnv* ev); + +public: // other cursor methods + + static void NilCursorStoreError(morkEnv* ev); + static void NonPortTableCursorTypeError(morkEnv* ev); + + morkEnv* CanUsePortTableCursor(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const; + + + morkRowSpace* NextSpace(morkEnv* ev); + morkTable* NextTable(morkEnv* ev); + + mork_bool SetRowScope(morkEnv* ev, mork_scope inRowScope); + mork_bool SetTableKind(morkEnv* ev, mork_kind inTableKind); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakPortTableCursor(morkPortTableCursor* me, + morkEnv* ev, morkPortTableCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongPortTableCursor(morkPortTableCursor* me, + morkEnv* ev, morkPortTableCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPORTTABLECURSOR_ */ diff --git a/db/mork/src/morkProbeMap.cpp b/db/mork/src/morkProbeMap.cpp new file mode 100644 index 0000000000..c2a9d4645b --- /dev/null +++ b/db/mork/src/morkProbeMap.cpp @@ -0,0 +1,1234 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is a port to NS Mork from public domain Mithril C++ sources. +// Note many code comments here come verbatim from cut-and-pasted Mithril. +// In many places, code is identical; Mithril versions stay public domain. +// Changes in porting are mainly class type and scalar type name changes. + +#include "nscore.h" + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKPROBEMAP_ +#include "morkProbeMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +/*============================================================================*/ +/* morkMapScratch */ + +void morkMapScratch::halt_map_scratch(morkEnv* ev) +{ + nsIMdbHeap* heap = sMapScratch_Heap; + + if ( heap ) + { + if ( sMapScratch_Keys ) + heap->Free(ev->AsMdbEnv(), sMapScratch_Keys); + if ( sMapScratch_Vals ) + heap->Free(ev->AsMdbEnv(), sMapScratch_Vals); + } +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +/*============================================================================*/ +/* morkProbeMap */ + +void morkProbeMap::ProbeMapBadTagError(morkEnv* ev) const +{ + ev->NewError("bad sProbeMap_Tag"); +} + +void morkProbeMap::WrapWithNoVoidSlotError(morkEnv* ev) const +{ + ev->NewError("wrap without void morkProbeMap slot"); +} + +void morkProbeMap::GrowFailsMaxFillError(morkEnv* ev) const +{ + ev->NewError("grow fails morkEnv > sMap_Fill"); +} + +void morkProbeMap::MapKeyIsNotIPError(morkEnv* ev) const +{ + ev->NewError("not sMap_KeyIsIP"); +} + +void morkProbeMap::MapValIsNotIPError(morkEnv* ev) const +{ + ev->NewError("not sMap_ValIsIP"); +} + +void morkProbeMap::rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch) +{ + mork_size keySize = sMap_KeySize; // size of every key bucket + mork_size valSize = sMap_ValSize; // size of every associated value + + mork_count slots = sMap_Slots; // number of new buckets + mork_u1* keys = sMap_Keys; // destination for rehashed keys + mork_u1* vals = sMap_Vals; // destination for any copied values + + mork_bool keyIsIP = ( keys && keySize == sizeof(mork_ip) && sMap_KeyIsIP ); + mork_bool valIsIP = ( vals && valSize == sizeof(mork_ip) && sMap_ValIsIP ); + + mork_count oldSlots = ioScratch->sMapScratch_Slots; // sMap_Slots + mork_u1* oldKeys = ioScratch->sMapScratch_Keys; // sMap_Keys + mork_u1* oldVals = ioScratch->sMapScratch_Vals; // sMap_Vals + mork_u1* end = oldKeys + (keySize * oldSlots); // one byte past last key + + mork_fill fill = 0; // let's count the actual fill for a double check + + while ( oldKeys < end ) // another old key bucket to rehash if non-nil? + { + if ( !this->ProbeMapIsKeyNil(ev, oldKeys) ) // need to rehash? + { + ++fill; // this had better match sMap_Fill when we are all done + mork_u4 hash = this->ProbeMapHashMapKey(ev, oldKeys); + + mork_pos i = hash % slots; // target hash bucket + mork_pos startPos = i; // remember start to detect + + mork_u1* k = keys + (i * keySize); + while ( !this->ProbeMapIsKeyNil(ev, k) ) + { + if ( ++i >= (mork_pos)slots ) // advanced past end? need to wrap around now? + i = 0; // wrap around to first slot in map's hash table + + if ( i == startPos ) // no void slots were found anywhere in map? + { + this->WrapWithNoVoidSlotError(ev); // should never happen + return; // this is bad, and we can't go on with the rehash + } + k = keys + (i * keySize); + } + if ( keyIsIP ) // int special case? + *((mork_ip*) k) = *((const mork_ip*) oldKeys); // fast bitwise copy + else + MORK_MEMCPY(k, oldKeys, keySize); // slow bitwise copy + + if ( oldVals ) // need to copy values as well? + { + mork_size valOffset = (i * valSize); + mork_u1* v = vals + valOffset; + mork_u1* ov = oldVals + valOffset; + if ( valIsIP ) // int special case? + *((mork_ip*) v) = *((const mork_ip*) ov); // fast bitwise copy + else + MORK_MEMCPY(v, ov, valSize); // slow bitwise copy + } + } + oldKeys += keySize; // advance to next key bucket in old map + } + if ( fill != sMap_Fill ) // is the recorded value of sMap_Fill wrong? + { + ev->NewWarning("fill != sMap_Fill"); + sMap_Fill = fill; + } +} + +mork_bool morkProbeMap::grow_probe_map(morkEnv* ev) +{ + if ( sMap_Heap ) // can we grow the map? + { + mork_num newSlots = ((sMap_Slots * 4) / 3) + 1; // +25% + morkMapScratch old; // a place to temporarily hold all the old arrays + if ( this->new_slots(ev, &old, newSlots) ) // have more? + { + ++sMap_Seed; // note the map has changed + this->rehash_old_map(ev, &old); + + if ( ev->Good() ) + { + mork_count slots = sMap_Slots; + mork_num emptyReserve = (slots / 7) + 1; // keep this many empty + mork_fill maxFill = slots - emptyReserve; // new max occupancy + if ( maxFill > sMap_Fill ) // new max is bigger than old occupancy? + sProbeMap_MaxFill = maxFill; // we can install new max for fill + else + this->GrowFailsMaxFillError(ev); // we have invariant failure + } + + if ( ev->Bad() ) // rehash failed? need to revert map to last state? + this->revert_map(ev, &old); // swap the vectors back again + + old.halt_map_scratch(ev); // remember to free the old arrays + } + } + else ev->OutOfMemoryError(); + + return ev->Good(); +} + +void morkProbeMap::revert_map(morkEnv* ev, morkMapScratch* ioScratch) +{ + mork_count tempSlots = ioScratch->sMapScratch_Slots; // sMap_Slots + mork_u1* tempKeys = ioScratch->sMapScratch_Keys; // sMap_Keys + mork_u1* tempVals = ioScratch->sMapScratch_Vals; // sMap_Vals + + ioScratch->sMapScratch_Slots = sMap_Slots; + ioScratch->sMapScratch_Keys = sMap_Keys; + ioScratch->sMapScratch_Vals = sMap_Vals; + + sMap_Slots = tempSlots; + sMap_Keys = tempKeys; + sMap_Vals = tempVals; +} + +void morkProbeMap::put_probe_kv(morkEnv* ev, + const void* inAppKey, const void* inAppVal, mork_pos inPos) +{ + mork_u1* mapVal = 0; + mork_u1* mapKey = 0; + + mork_num valSize = sMap_ValSize; + if ( valSize && inAppVal ) // map holds values? caller sends value? + { + mork_u1* val = sMap_Vals + (valSize * inPos); + if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case? + *((mork_ip*) val) = *((const mork_ip*) inAppVal); + else + mapVal = val; // show possible need to call ProbeMapPushIn() + } + if ( inAppKey ) // caller sends the key? + { + mork_num keySize = sMap_KeySize; + mork_u1* key = sMap_Keys + (keySize * inPos); + if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case? + *((mork_ip*) key) = *((const mork_ip*) inAppKey); + else + mapKey = key; // show possible need to call ProbeMapPushIn() + } + else + ev->NilPointerError(); + + if ( ( inAppVal && mapVal ) || ( inAppKey && mapKey ) ) + this->ProbeMapPushIn(ev, inAppKey, inAppVal, mapKey, mapVal); + + if ( sMap_Fill > sProbeMap_MaxFill ) + this->grow_probe_map(ev); +} + +void morkProbeMap::get_probe_kv(morkEnv* ev, + void* outAppKey, void* outAppVal, mork_pos inPos) const +{ + const mork_u1* mapVal = 0; + const mork_u1* mapKey = 0; + + mork_num valSize = sMap_ValSize; + if ( valSize && outAppVal ) // map holds values? caller wants value? + { + const mork_u1* val = sMap_Vals + (valSize * inPos); + if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case? + *((mork_ip*) outAppVal) = *((const mork_ip*) val); + else + mapVal = val; // show possible need to call ProbeMapPullOut() + } + if ( outAppKey ) // caller wants the key? + { + mork_num keySize = sMap_KeySize; + const mork_u1* key = sMap_Keys + (keySize * inPos); + if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case? + *((mork_ip*) outAppKey) = *((const mork_ip*) key); + else + mapKey = key; // show possible need to call ProbeMapPullOut() + } + if ( ( outAppVal && mapVal ) || ( outAppKey && mapKey ) ) + this->ProbeMapPullOut(ev, mapKey, mapVal, outAppKey, outAppVal); +} + +mork_test +morkProbeMap::find_key_pos(morkEnv* ev, const void* inAppKey, + mork_u4 inHash, mork_pos* outPos) const +{ + mork_u1* k = sMap_Keys; // array of keys, each of size sMap_KeySize + mork_num size = sMap_KeySize; // number of bytes in each key + mork_count slots = sMap_Slots; // total number of key buckets + mork_pos i = inHash % slots; // target hash bucket + mork_pos startPos = i; // remember start to detect + + mork_test outTest = this->MapTest(ev, k + (i * size), inAppKey); + while ( outTest == morkTest_kMiss ) + { + if ( ++i >= (mork_pos)slots ) // advancing goes beyond end? need to wrap around now? + i = 0; // wrap around to first slot in map's hash table + + if ( i == startPos ) // no void slots were found anywhere in map? + { + this->WrapWithNoVoidSlotError(ev); // should never happen + break; // end loop on kMiss; note caller expects either kVoid or kHit + } + outTest = this->MapTest(ev, k + (i * size), inAppKey); + } + *outPos = i; + + return outTest; +} + +void morkProbeMap::probe_map_lazy_init(morkEnv* ev) +{ + if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action? + { + // The constructor cannot successfully call virtual ProbeMapClearKey(), + // so we lazily do so now, when we add the first member to the map. + + mork_u1* keys = sMap_Keys; + if ( keys ) // okay to call lazy virtual clear method on new map keys? + { + if ( sProbeMap_ZeroIsClearKey ) // zero is good enough to clear keys? + { + mork_num keyVolume = sMap_Slots * sMap_KeySize; + if ( keyVolume ) + MORK_MEMSET(keys, 0, keyVolume); + } + else + this->ProbeMapClearKey(ev, keys, sMap_Slots); + } + else + this->MapNilKeysError(ev); + } + sProbeMap_LazyClearOnAdd = 0; // don't do this ever again +} + +mork_bool +morkProbeMap::MapAtPut(morkEnv* ev, + const void* inAppKey, const void* inAppVal, + void* outAppKey, void* outAppVal) +{ + mork_bool outPut = morkBool_kFalse; + + if ( this->GoodProbeMap() ) /* looks good? */ + { + if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action? + this->probe_map_lazy_init(ev); + + if ( ev->Good() ) + { + mork_pos slotPos = 0; + mork_u4 hash = this->MapHash(ev, inAppKey); + mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); + outPut = ( test == morkTest_kHit ); + + if ( outPut ) // replacing an old assoc? no change in member count? + { + if ( outAppKey || outAppVal ) /* copy old before cobber? */ + this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); + } + else // adding a new assoc increases membership by one + { + ++sMap_Fill; /* one more member in the collection */ + } + + if ( test != morkTest_kMiss ) /* found slot to hold new assoc? */ + { + ++sMap_Seed; /* note the map has changed */ + this->put_probe_kv(ev, inAppKey, inAppVal, slotPos); + } + } + } + else this->ProbeMapBadTagError(ev); + + return outPut; +} + +mork_bool +morkProbeMap::MapAt(morkEnv* ev, const void* inAppKey, + void* outAppKey, void* outAppVal) +{ + if ( this->GoodProbeMap() ) /* looks good? */ + { + if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action? + this->probe_map_lazy_init(ev); + + mork_pos slotPos = 0; + mork_u4 hash = this->MapHash(ev, inAppKey); + mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos); + if ( test == morkTest_kHit ) /* found an assoc pair for inAppKey? */ + { + this->get_probe_kv(ev, outAppKey, outAppVal, slotPos); + return morkBool_kTrue; + } + } + else this->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_num +morkProbeMap::MapCutAll(morkEnv* ev) +{ + mork_num outCutAll = 0; + + if ( this->GoodProbeMap() ) /* looks good? */ + { + outCutAll = sMap_Fill; /* number of members cut, which is all of them */ + + if ( sMap_Keys && !sProbeMap_ZeroIsClearKey ) + this->ProbeMapClearKey(ev, sMap_Keys, sMap_Slots); + + sMap_Fill = 0; /* map now has no members */ + } + else this->ProbeMapBadTagError(ev); + + return outCutAll; +} + +// { ===== node interface ===== + +/*virtual*/ +morkProbeMap::~morkProbeMap() // assert NodeStop() finished earlier +{ + MORK_ASSERT(sMap_Keys==0); + MORK_ASSERT(sProbeMap_Tag==0); +} + +/*public virtual*/ void +morkProbeMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseProbeMap(ev); + this->MarkShut(); + } +} + +void morkProbeMap::CloseProbeMap(morkEnv* ev) +{ + if ( this->IsNode() ) + { + nsIMdbHeap* heap = sMap_Heap; + if ( heap ) // able to free map arrays? + { + void* block = sMap_Keys; + if ( block ) + { + heap->Free(ev->AsMdbEnv(), block); + sMap_Keys = 0; + } + + block = sMap_Vals; + if ( block ) + { + heap->Free(ev->AsMdbEnv(), block); + sMap_Vals = 0; + } + } + sMap_Keys = 0; + sMap_Vals = 0; + + this->CloseNode(ev); + sProbeMap_Tag = 0; + sProbeMap_MaxFill = 0; + + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +void* +morkProbeMap::clear_alloc(morkEnv* ev, mork_size inSize) +{ + void* p = 0; + nsIMdbHeap* heap = sMap_Heap; + if ( heap ) + { + if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p ) + { + MORK_MEMSET(p, 0, inSize); + return p; + } + } + else + ev->NilPointerError(); + + return (void*) 0; +} + +/*| map_new_keys: allocate an array of inSlots new keys filled with zero. +**| (cf IronDoc's FeHashTable_new_keys()) +|*/ +mork_u1* +morkProbeMap::map_new_keys(morkEnv* ev, mork_num inSlots) +{ + mork_num size = inSlots * sMap_KeySize; + return (mork_u1*) this->clear_alloc(ev, size); +} + +/*| map_new_vals: allocate an array of inSlots new values filled with zero. +**| When values are zero sized, we just return a null pointer. +**| +**| (cf IronDoc's FeHashTable_new_values()) +|*/ +mork_u1* +morkProbeMap::map_new_vals(morkEnv* ev, mork_num inSlots) +{ + mork_u1* values = 0; + mork_num size = inSlots * sMap_ValSize; + if ( size ) + values = (mork_u1*) this->clear_alloc(ev, size); + return values; +} + + +void morkProbeMap::MapSeedOutOfSyncError(morkEnv* ev) +{ + ev->NewError("sMap_Seed out of sync"); +} + +void morkProbeMap::MapFillUnderflowWarning(morkEnv* ev) +{ + ev->NewWarning("sMap_Fill underflow"); +} + +void morkProbeMap::MapNilKeysError(morkEnv* ev) +{ + ev->NewError("nil sMap_Keys"); +} + +void morkProbeMap::MapZeroKeySizeError(morkEnv* ev) +{ + ev->NewError("zero sMap_KeySize"); +} + +/*static*/ +void morkProbeMap::ProbeMapCutError(morkEnv* ev) +{ + ev->NewError("morkProbeMap cannot cut"); +} + + +void morkProbeMap::init_probe_map(morkEnv* ev, mork_size inSlots) +{ + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call init_probe_map() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + if ( ev->Good() ) + { + morkMapScratch old; + + if ( inSlots < 7 ) // capacity too small? + inSlots = 7; // increase to reasonable minimum + else if ( inSlots > (128 * 1024) ) // requested capacity too big? + inSlots = (128 * 1024); // decrease to reasonable maximum + + if ( this->new_slots(ev, &old, inSlots) ) + sProbeMap_Tag = morkProbeMap_kTag; + + mork_count slots = sMap_Slots; + mork_num emptyReserve = (slots / 7) + 1; // keep this many empty + sProbeMap_MaxFill = slots - emptyReserve; + + MORK_MEMSET(&old, 0, sizeof(morkMapScratch)); // don't bother halting + } +} + +mork_bool +morkProbeMap::new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots) +{ + mork_bool outNew = morkBool_kFalse; + + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call new_slots() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + // allocate every new array before we continue: + mork_u1* newKeys = this->map_new_keys(ev, inSlots); + mork_u1* newVals = this->map_new_vals(ev, inSlots); + + // okay for newVals to be null when values are zero sized? + mork_bool okayValues = ( newVals || !sMap_ValSize ); + + if ( newKeys && okayValues ) + { + outNew = morkBool_kTrue; // we created every array needed + + // init mapScratch using slots from current map: + old->sMapScratch_Heap = sMap_Heap; + + old->sMapScratch_Slots = sMap_Slots; + old->sMapScratch_Keys = sMap_Keys; + old->sMapScratch_Vals = sMap_Vals; + + // replace all map array slots using the newly allocated members: + ++sMap_Seed; // the map has changed + sMap_Keys = newKeys; + sMap_Vals = newVals; + sMap_Slots = inSlots; + } + else // free any allocations if only partially successful + { + nsIMdbHeap* heap = sMap_Heap; + if ( newKeys ) + heap->Free(ev->AsMdbEnv(), newKeys); + if ( newVals ) + heap->Free(ev->AsMdbEnv(), newVals); + + MORK_MEMSET(old, 0, sizeof(morkMapScratch)); // zap scratch space + } + + return outNew; +} + +void +morkProbeMap::clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap) +{ + sProbeMap_Tag = 0; + sMap_Seed = 0; + sMap_Slots = 0; + sMap_Fill = 0; + sMap_Keys = 0; + sMap_Vals = 0; + sProbeMap_MaxFill = 0; + + sMap_Heap = ioMapHeap; + if ( !ioMapHeap ) + ev->NilPointerError(); +} + +morkProbeMap::morkProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, + mork_size inKeySize, mork_size inValSize, + nsIMdbHeap* ioMapHeap, mork_size inSlots, + mork_bool inZeroIsClearKey) + +: morkNode(ev, inUsage, ioNodeHeap) +, sMap_Heap( ioMapHeap ) + +, sMap_Keys( 0 ) +, sMap_Vals( 0 ) + +, sMap_Seed( 0 ) // change count of members or structure + +, sMap_Slots( 0 ) // count of slots in the hash table +, sMap_Fill( 0 ) // number of used slots in the hash table + +, sMap_KeySize( 0 ) // size of each key (cannot be zero) +, sMap_ValSize( 0 ) // size of each val (zero allowed) + +, sMap_KeyIsIP( morkBool_kFalse ) // sMap_KeySize == sizeof(mork_ip) +, sMap_ValIsIP( morkBool_kFalse ) // sMap_ValSize == sizeof(mork_ip) + +, sProbeMap_MaxFill( 0 ) +, sProbeMap_LazyClearOnAdd( 0 ) +, sProbeMap_ZeroIsClearKey( inZeroIsClearKey ) +, sProbeMap_Tag( 0 ) +{ + // Note we cannot successfully call virtual ProbeMapClearKey() when we + // call init_probe_map() inside the constructor; so we leave this problem + // to the caller. (The constructor will call ProbeMapClearKey() later + // after setting a suitable lazy flag to show this action is pending.) + + if ( ev->Good() ) + { + this->clear_probe_map(ev, ioMapHeap); + if ( ev->Good() ) + { + sMap_KeySize = inKeySize; + sMap_ValSize = inValSize; + sMap_KeyIsIP = ( inKeySize == sizeof(mork_ip) ); + sMap_ValIsIP = ( inValSize == sizeof(mork_ip) ); + + this->init_probe_map(ev, inSlots); + if ( ev->Good() ) + { + if ( !inZeroIsClearKey ) // must lazy clear later with virtual method? + sProbeMap_LazyClearOnAdd = morkProbeMap_kLazyClearOnAdd; + + mNode_Derived = morkDerived_kProbeMap; + } + } + } +} + +/*============================================================================*/ + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkProbeMap::MapTest(morkEnv* ev, + const void* inMapKey, const void* inAppKey) const + // Note inMapKey is always a key already stored in the map, while inAppKey + // is always a method argument parameter from a client method call. + // This matters the most in morkProbeMap subclasses, which have the + // responsibility of putting 'app' keys into slots for 'map' keys, and + // the bit pattern representation might be different in such cases. + // morkTest_kHit means that inMapKey equals inAppKey (and this had better + // also imply that hash(inMapKey) == hash(inAppKey)). + // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this + // implies nothing at all about hash(inMapKey) and hash(inAppKey)). + // morkTest_kVoid means that inMapKey is not a valid key bit pattern, + // which means that key slot in the map is not being used. Note that + // kVoid is only expected as a return value in morkProbeMap subclasses, + // because morkProbeMap must ask whether a key slot is used or not. + // morkChainMap however, always knows when a key slot is used, so only + // key slots expected to have valid bit patterns will be presented to + // the MapTest() methods for morkChainMap subclasses. + // + // NOTE: it is very important that subclasses correctly return the value + // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern + // that means the slot is not being used, because this is the only way a + // probe map can terminate an unsuccessful search for a key in the map. +{ + mork_size keySize = sMap_KeySize; + if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) + { + mork_ip mapKey = *((const mork_ip*) inMapKey); + if ( mapKey == *((const mork_ip*) inAppKey) ) + return morkTest_kHit; + else + { + return ( mapKey )? morkTest_kMiss : morkTest_kVoid; + } + } + else + { + mork_bool allSame = morkBool_kTrue; + mork_bool allZero = morkBool_kTrue; + const mork_u1* ak = (const mork_u1*) inAppKey; + const mork_u1* mk = (const mork_u1*) inMapKey; + const mork_u1* end = mk + keySize; + --mk; // prepare for preincrement: + while ( ++mk < end ) + { + mork_u1 byte = *mk; + if ( byte ) // any nonzero byte in map key means slot is not nil? + allZero = morkBool_kFalse; + if ( byte != *ak++ ) // bytes differ in map and app keys? + allSame = morkBool_kFalse; + } + if ( allSame ) + return morkTest_kHit; + else + return ( allZero )? morkTest_kVoid : morkTest_kMiss; + } +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const +{ + mork_size keySize = sMap_KeySize; + if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) + { + return *((const mork_ip*) inAppKey); + } + else + { + const mork_u1* key = (const mork_u1*) inAppKey; + const mork_u1* end = key + keySize; + --key; // prepare for preincrement: + while ( ++key < end ) + { + if ( *key ) // any nonzero byte in map key means slot is not nil? + return morkBool_kFalse; + } + return morkBool_kTrue; + } + return (mork_u4) NS_PTR_TO_INT32(inAppKey); +} + + +/*============================================================================*/ + +/*virtual*/ mork_u4 +morkProbeMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const + // ProbeMapHashMapKey() does logically the same thing as MapHash(), and + // the default implementation actually calls virtual MapHash(). However, + // Subclasses must override this method whenever the formats of keys in + // the map differ from app keys outside the map, because MapHash() only + // works on keys in 'app' format, while ProbeMapHashMapKey() only works + // on keys in 'map' format. This method is called in order to rehash all + // map keys when a map is grown, and this causes all old map members to + // move into new slot locations. + // + // Note it is absolutely imperative that a hash for a key in 'map' format + // be exactly the same the hash of the same key in 'app' format, or else + // maps will seem corrupt later when keys in 'app' format cannot be found. +{ + return this->MapHash(ev, inMapKey); +} + +/*virtual*/ mork_bool +morkProbeMap::ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey) + // ProbeMapIsKeyNil() must say whether the representation of logical 'nil' + // is currently found inside the key at ioMapKey, for a key found within + // the map. The the map iterator uses this method to find map keys that + // are actually being used for valid map associations; otherwise the + // iterator cannot determine which map slots actually denote used keys. + // The default method version returns true if all the bits equal zero. +{ + if ( sMap_KeySize == sizeof(mork_ip) && sMap_KeyIsIP ) + { + return !*((const mork_ip*) ioMapKey); + } + else + { + const mork_u1* key = (const mork_u1*) ioMapKey; + const mork_u1* end = key + sMap_KeySize; + --key; // prepare for preincrement: + while ( ++key < end ) + { + if ( *key ) // any nonzero byte in map key means slot is not nil? + return morkBool_kFalse; + } + return morkBool_kTrue; + } +} + +/*virtual*/ void +morkProbeMap::ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + void* ioMapKey, mork_count inKeyCount) // array of keys inside map + // ProbeMapClearKey() must put some representation of logical 'nil' into + // every key slot in the map, such that MapTest() will later recognize + // that this bit pattern shows each key slot is not actually being used. + // + // This method is typically called whenever the map is either created or + // grown into a larger size, where ioMapKey is a pointer to an array of + // inKeyCount keys, where each key is this->MapKeySize() bytes in size. + // Note that keys are assumed immediately adjacent with no padding, so + // if any alignment requirements must be met, then subclasses should have + // already accounted for this when specifying a key size in the map. + // + // Since this method will be called when a map is being grown in size, + // nothing should be assumed about the state slots of the map, since the + // ioMapKey array might not yet live in sMap_Keys, and the array length + // inKeyCount might not yet live in sMap_Slots. However, the value kept + // in sMap_KeySize never changes, so this->MapKeySize() is always correct. +{ + if ( ioMapKey && inKeyCount ) + { + MORK_MEMSET(ioMapKey, 0, (inKeyCount * sMap_KeySize)); + } + else + ev->NilPointerWarning(); +} + +/*virtual*/ void +morkProbeMap::ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + const void* inAppKey, const void* inAppVal, // (key,val) outside map + void* outMapKey, void* outMapVal) // (key,val) inside map + // This method actually puts keys and vals in the map in suitable format. + // + // ProbeMapPushIn() must copy a caller key and value in 'app' format + // into the map slots provided, which are in 'map' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input app key in output map format. + // + // Do NOT write more than this->MapKeySize() bytes of a map key, or more + // than this->MapValSize() bytes of a map val, or corruption might ensue. + // + // The inAppKey and inAppVal parameters are the same ones passed into a + // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones + // determined by how the map currently positions key inAppKey in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inAppKey or outMapKey is nil, and do + // no val move at all when either inAppVal or outMapVal is nil. Note that + // outMapVal should always be nil when this->MapValSize() is nil. +{ +} + +/*virtual*/ void +morkProbeMap::ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + const void* inMapKey, const void* inMapVal, // (key,val) inside map + void* outAppKey, void* outAppVal) const // (key,val) outside map + // This method actually gets keys and vals from the map in suitable format. + // + // ProbeMapPullOut() must copy a key and val in 'map' format into the + // caller key and val slots provided, which are in 'app' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input map key in output app format. + // + // The outAppKey and outAppVal parameters are the same ones passed into a + // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are + // determined by how the map currently positions the target key in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inMapKey or outAppKey is nil, and do + // no val move at all when either inMapVal or outAppVal is nil. Note that + // inMapVal should always be nil when this->MapValSize() is nil. +{ +} + + +/*============================================================================*/ +/* morkProbeMapIter */ + +morkProbeMapIter::morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) +: sProbeMapIter_Map( 0 ) +, sProbeMapIter_Seed( 0 ) +, sProbeMapIter_HereIx( morkProbeMapIter_kBeforeIx ) +{ + if ( ioMap ) + { + if ( ioMap->GoodProbeMap() ) + { + if ( ioMap->need_lazy_init() ) // pending lazy action? + ioMap->probe_map_lazy_init(ev); + + sProbeMapIter_Map = ioMap; + sProbeMapIter_Seed = ioMap->sMap_Seed; + } + else ioMap->ProbeMapBadTagError(ev); + } + else ev->NilPointerError(); +} + +void morkProbeMapIter::CloseMapIter(morkEnv* ev) +{ + MORK_USED_1(ev); + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; +} + +morkProbeMapIter::morkProbeMapIter( ) +// zero most slots; caller must call InitProbeMapIter() +{ + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; +} + +void morkProbeMapIter::InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap) +{ + sProbeMapIter_Map = 0; + sProbeMapIter_Seed = 0; + + sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx; + + if ( ioMap ) + { + if ( ioMap->GoodProbeMap() ) + { + if ( ioMap->need_lazy_init() ) // pending lazy action? + ioMap->probe_map_lazy_init(ev); + + sProbeMapIter_Map = ioMap; + sProbeMapIter_Seed = ioMap->sMap_Seed; + } + else ioMap->ProbeMapBadTagError(ev); + } + else ev->NilPointerError(); +} + +mork_bool morkProbeMapIter::IterFirst(morkEnv* ev, + void* outAppKey, void* outAppVal) +{ + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done + morkProbeMap* map = sProbeMapIter_Map; + + if ( map && map->GoodProbeMap() ) /* looks good? */ + { + sProbeMapIter_Seed = map->sMap_Seed; /* sync the seeds */ + + mork_u1* k = map->sMap_Keys; // array of keys, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + mork_count slots = map->sMap_Slots; // total number of key buckets + mork_pos here = 0; // first hash bucket + + while ( here < (mork_pos)slots ) + { + if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) ) + { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + + sProbeMapIter_HereIx = (mork_i4) here; + return morkBool_kTrue; + } + ++here; // next bucket + } + } + else map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_bool morkProbeMapIter::IterNext(morkEnv* ev, + void* outAppKey, void* outAppVal) +{ + morkProbeMap* map = sProbeMapIter_Map; + + if ( map && map->GoodProbeMap() ) /* looks good? */ + { + if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */ + { + if ( sProbeMapIter_HereIx != morkProbeMapIter_kAfterIx ) + { + mork_pos here = (mork_pos) sProbeMapIter_HereIx; + if ( sProbeMapIter_HereIx < 0 ) + here = 0; + else + ++here; + + sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done + + mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + mork_count slots = map->sMap_Slots; // total number of key buckets + + while ( here < (mork_pos)slots ) + { + if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) ) + { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + + sProbeMapIter_HereIx = (mork_i4) here; + return morkBool_kTrue; + } + ++here; // next bucket + } + } + } + else map->MapSeedOutOfSyncError(ev); + } + else map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_bool morkProbeMapIter::IterHere(morkEnv* ev, + void* outAppKey, void* outAppVal) +{ + morkProbeMap* map = sProbeMapIter_Map; + + if ( map && map->GoodProbeMap() ) /* looks good? */ + { + if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */ + { + mork_pos here = (mork_pos) sProbeMapIter_HereIx; + mork_count slots = map->sMap_Slots; // total number of key buckets + if ( sProbeMapIter_HereIx >= 0 && (here < (mork_pos)slots)) + { + mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize + mork_num size = map->sMap_KeySize; // number of bytes in each key + + if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) ) + { + map->get_probe_kv(ev, outAppKey, outAppVal, here); + return morkBool_kTrue; + } + } + } + else map->MapSeedOutOfSyncError(ev); + } + else map->ProbeMapBadTagError(ev); + + return morkBool_kFalse; +} + +mork_change* +morkProbeMapIter::First(morkEnv* ev, void* outKey, void* outVal) +{ + if ( this->IterFirst(ev, outKey, outVal) ) + return &sProbeMapIter_Change; + + return (mork_change*) 0; +} + +mork_change* +morkProbeMapIter::Next(morkEnv* ev, void* outKey, void* outVal) +{ + if ( this->IterNext(ev, outKey, outVal) ) + return &sProbeMapIter_Change; + + return (mork_change*) 0; +} + +mork_change* +morkProbeMapIter::Here(morkEnv* ev, void* outKey, void* outVal) +{ + if ( this->IterHere(ev, outKey, outVal) ) + return &sProbeMapIter_Change; + + return (mork_change*) 0; +} + +mork_change* +morkProbeMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal) +{ + morkProbeMap::ProbeMapCutError(ev); + + return (mork_change*) 0; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. +// (Note the implied assumption that zero is never a good value pattern.) + +void* morkProbeMapIter::IterFirstVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_ValIsIP ) + { + void* v = 0; + this->IterFirst(ev, outKey, &v); + return v; + } + else + map->MapValIsNotIPError(ev); + } + return (void*) 0; +} + +void* morkProbeMapIter::IterNextVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_ValIsIP ) + { + void* v = 0; + this->IterNext(ev, outKey, &v); + return v; + } + else + map->MapValIsNotIPError(ev); + } + return (void*) 0; +} + +void* morkProbeMapIter::IterHereVal(morkEnv* ev, void* outKey) +// equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_ValIsIP ) + { + void* v = 0; + this->IterHere(ev, outKey, &v); + return v; + } + else + map->MapValIsNotIPError(ev); + } + return (void*) 0; +} + +// NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. +// (Note the implied assumption that zero is never a good key pattern.) + +void* morkProbeMapIter::IterFirstKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_KeyIsIP ) + { + void* k = 0; + this->IterFirst(ev, &k, (void*) 0); + return k; + } + else + map->MapKeyIsNotIPError(ev); + } + return (void*) 0; +} + +void* morkProbeMapIter::IterNextKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_KeyIsIP ) + { + void* k = 0; + this->IterNext(ev, &k, (void*) 0); + return k; + } + else + map->MapKeyIsNotIPError(ev); + } + return (void*) 0; +} + +void* morkProbeMapIter::IterHereKey(morkEnv* ev) +// equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } +{ + morkProbeMap* map = sProbeMapIter_Map; + if ( map ) + { + if ( map->sMap_KeyIsIP ) + { + void* k = 0; + this->IterHere(ev, &k, (void*) 0); + return k; + } + else + map->MapKeyIsNotIPError(ev); + } + return (void*) 0; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkProbeMap.h b/db/mork/src/morkProbeMap.h new file mode 100644 index 0000000000..8b9b23d21e --- /dev/null +++ b/db/mork/src/morkProbeMap.h @@ -0,0 +1,431 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This code is a port to NS Mork from public domain Mithril C++ sources. +// Note many code comments here come verbatim from cut-and-pasted Mithril. +// In many places, code is identical; Mithril versions stay public domain. +// Changes in porting are mainly class type and scalar type name changes. + +#ifndef _MORKPROBEMAP_ +#define _MORKPROBEMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkMapScratch { // utility class used by map subclasses +public: + nsIMdbHeap* sMapScratch_Heap; // cached sMap_Heap + mork_count sMapScratch_Slots; // cached sMap_Slots + + mork_u1* sMapScratch_Keys; // cached sMap_Keys + mork_u1* sMapScratch_Vals; // cached sMap_Vals + +public: + void halt_map_scratch(morkEnv* ev); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kProbeMap 0x7072 /* ascii 'pr' */ +#define morkProbeMap_kTag 0x70724D50 /* ascii 'prMP' */ + +#define morkProbeMap_kLazyClearOnAdd ((mork_u1) 'c') + +class morkProbeMap: public morkNode { + +protected: + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +protected: + // { begin morkMap slots + nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space + + mork_u1* sMap_Keys; + mork_u1* sMap_Vals; + + mork_count sMap_Seed; // change count of members or structure + + mork_count sMap_Slots; // count of slots in the hash table + mork_fill sMap_Fill; // number of used slots in the hash table + + mork_size sMap_KeySize; // size of each key (cannot be zero) + mork_size sMap_ValSize; // size of each val (zero allowed) + + mork_bool sMap_KeyIsIP; // sMap_KeySize == sizeof(mork_ip) + mork_bool sMap_ValIsIP; // sMap_ValSize == sizeof(mork_ip) + mork_u1 sMap_Pad[ 2 ]; // for u4 alignment + // } end morkMap slots + + friend class morkProbeMapIter; // for access to protected slots + +public: // getters + mork_count MapSeed() const { return sMap_Seed; } + + mork_count MapSlots() const { return sMap_Slots; } + mork_fill MapFill() const { return sMap_Fill; } + + mork_size MapKeySize() const { return sMap_KeySize; } + mork_size MapValSize() const { return sMap_ValSize; } + + mork_bool MapKeyIsIP() const { return sMap_KeyIsIP; } + mork_bool MapValIsIP() const { return sMap_ValIsIP; } + +protected: // slots + // { begin morkProbeMap slots + + mork_fill sProbeMap_MaxFill; // max sMap_Fill before map must grow + + mork_u1 sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd + mork_bool sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys + mork_u1 sProbeMap_Pad[ 2 ]; // for u4 alignment + + mork_u4 sProbeMap_Tag; + + // } end morkProbeMap slots + +public: // lazy clear on add + + mork_bool need_lazy_init() const + { return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; } + +public: // typing + mork_bool GoodProbeMap() const + { return sProbeMap_Tag == morkProbeMap_kTag; } + +protected: // utilities + + void* clear_alloc(morkEnv* ev, mork_size inSize); + + mork_u1* map_new_vals(morkEnv* ev, mork_num inSlots); + mork_u1* map_new_keys(morkEnv* ev, mork_num inSlots); + + void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap); + void init_probe_map(morkEnv* ev, mork_size inSlots); + void probe_map_lazy_init(morkEnv* ev); + + mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots); + + mork_test find_key_pos(morkEnv* ev, const void* inAppKey, + mork_u4 inHash, mork_pos* outPos) const; + + void put_probe_kv(morkEnv* ev, + const void* inAppKey, const void* inAppVal, mork_pos inPos); + void get_probe_kv(morkEnv* ev, + void* outAppKey, void* outAppVal, mork_pos inPos) const; + + mork_bool grow_probe_map(morkEnv* ev); + void rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch); + void revert_map(morkEnv* ev, morkMapScratch* ioScratch); + +public: // errors + void ProbeMapBadTagError(morkEnv* ev) const; + void WrapWithNoVoidSlotError(morkEnv* ev) const; + void GrowFailsMaxFillError(morkEnv* ev) const; + void MapKeyIsNotIPError(morkEnv* ev) const; + void MapValIsNotIPError(morkEnv* ev) const; + + void MapNilKeysError(morkEnv* ev); + void MapZeroKeySizeError(morkEnv* ev); + + void MapSeedOutOfSyncError(morkEnv* ev); + void MapFillUnderflowWarning(morkEnv* ev); + + static void ProbeMapCutError(morkEnv* ev); + + // { ===== begin morkMap methods ===== +public: + + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const; + // Note inMapKey is always a key already stored in the map, while inAppKey + // is always a method argument parameter from a client method call. + // This matters the most in morkProbeMap subclasses, which have the + // responsibility of putting 'app' keys into slots for 'map' keys, and + // the bit pattern representation might be different in such cases. + // morkTest_kHit means that inMapKey equals inAppKey (and this had better + // also imply that hash(inMapKey) == hash(inAppKey)). + // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this + // implies nothing at all about hash(inMapKey) and hash(inAppKey)). + // morkTest_kVoid means that inMapKey is not a valid key bit pattern, + // which means that key slot in the map is not being used. Note that + // kVoid is only expected as a return value in morkProbeMap subclasses, + // because morkProbeMap must ask whether a key slot is used or not. + // morkChainMap however, always knows when a key slot is used, so only + // key slots expected to have valid bit patterns will be presented to + // the MapTest() methods for morkChainMap subclasses. + // + // NOTE: it is very important that subclasses correctly return the value + // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern + // that means the slot is not being used, because this is the only way a + // probe map can terminate an unsuccessful search for a key in the map. + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const; + + virtual mork_bool + MapAtPut(morkEnv* ev, const void* inAppKey, const void* inAppVal, + void* outAppKey, void* outAppVal); + + virtual mork_bool + MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, void* outAppVal); + + virtual mork_num + MapCutAll(morkEnv* ev); + // } ===== end morkMap methods ===== + + + // { ===== begin morkProbeMap methods ===== +public: + + virtual mork_u4 + ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const; + // ProbeMapHashMapKey() does logically the same thing as MapHash(), and + // the default implementation actually calls virtual MapHash(). However, + // Subclasses must override this method whenever the formats of keys in + // the map differ from app keys outside the map, because MapHash() only + // works on keys in 'app' format, while ProbeMapHashMapKey() only works + // on keys in 'map' format. This method is called in order to rehash all + // map keys when a map is grown, and this causes all old map members to + // move into new slot locations. + // + // Note it is absolutely imperative that a hash for a key in 'map' format + // be exactly the same the hash of the same key in 'app' format, or else + // maps will seem corrupt later when keys in 'app' format cannot be found. + + virtual mork_bool + ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + // ProbeMapIsKeyNil() must say whether the representation of logical 'nil' + // is currently found inside the key at ioMapKey, for a key found within + // the map. The the map iterator uses this method to find map keys that + // are actually being used for valid map associations; otherwise the + // iterator cannot determine which map slots actually denote used keys. + // The default method version returns true if all the bits equal zero. + + virtual void + ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + void* ioMapKey, mork_count inKeyCount); // array of keys inside map + // ProbeMapClearKey() must put some representation of logical 'nil' into + // every key slot in the map, such that MapTest() will later recognize + // that this bit pattern shows each key slot is not actually being used. + // + // This method is typically called whenever the map is either created or + // grown into a larger size, where ioMapKey is a pointer to an array of + // inKeyCount keys, where each key is this->MapKeySize() bytes in size. + // Note that keys are assumed immediately adjacent with no padding, so + // if any alignment requirements must be met, then subclasses should have + // already accounted for this when specifying a key size in the map. + // + // Since this method will be called when a map is being grown in size, + // nothing should be assumed about the state slots of the map, since the + // ioMapKey array might not yet live in sMap_Keys, and the array length + // inKeyCount might not yet live in sMap_Slots. However, the value kept + // in sMap_KeySize never changes, so this->MapKeySize() is always correct. + + virtual void + ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + const void* inAppKey, const void* inAppVal, // (key,val) outside map + void* outMapKey, void* outMapVal); // (key,val) inside map + // This method actually puts keys and vals in the map in suitable format. + // + // ProbeMapPushIn() must copy a caller key and value in 'app' format + // into the map slots provided, which are in 'map' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input app key in output map format. + // + // Do NOT write more than this->MapKeySize() bytes of a map key, or more + // than this->MapValSize() bytes of a map val, or corruption might ensue. + // + // The inAppKey and inAppVal parameters are the same ones passed into a + // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones + // determined by how the map currently positions key inAppKey in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inAppKey or outMapKey is nil, and do + // no val move at all when either inAppVal or outMapVal is nil. Note that + // outMapVal should always be nil when this->MapValSize() is nil. + + virtual void + ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + const void* inMapKey, const void* inMapVal, // (key,val) inside map + void* outAppKey, void* outAppVal) const; // (key,val) outside map + // This method actually gets keys and vals from the map in suitable format. + // + // ProbeMapPullOut() must copy a key and val in 'map' format into the + // caller key and val slots provided, which are in 'app' format. When the + // 'app' and 'map' formats are identical, then this is just a bitwise + // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes, + // and this is exactly what the default implementation performs. However, + // if 'app' and 'map' formats are different, and MapTest() depends on this + // difference in format, then subclasses must override this method to do + // whatever is necessary to store the input map key in output app format. + // + // The outAppKey and outAppVal parameters are the same ones passed into a + // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are + // determined by how the map currently positions the target key in the map. + // + // Note any key or val parameter can be a null pointer, in which case + // this method must do nothing with those parameters. In particular, do + // no key move at all when either inMapKey or outAppKey is nil, and do + // no val move at all when either inMapVal or outAppVal is nil. Note that + // inMapVal should always be nil when this->MapValSize() is nil. + + // } ===== end morkProbeMap methods ===== + + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseProbeMap() only if open + virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier + +public: // morkProbeMap construction & destruction + morkProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, + mork_size inKeySize, mork_size inValSize, + nsIMdbHeap* ioMapHeap, mork_size inSlots, + mork_bool inZeroIsClearKey); + + void CloseProbeMap(morkEnv* ev); // called by + +public: // dynamic type identification + mork_bool IsProbeMap() const + { return IsNode() && mNode_Derived == morkDerived_kProbeMap; } +// } ===== end morkNode methods ===== + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakMap(morkMap* me, + morkEnv* ev, morkMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongMap(morkMap* me, + morkEnv* ev, morkMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +/*============================================================================*/ +/* morkProbeMapIter */ + +#define morkProbeMapIter_kBeforeIx ((mork_i4) -1) /* before first member */ +#define morkProbeMapIter_kAfterIx ((mork_i4) -2) /* after last member */ + +class morkProbeMapIter { + +protected: + morkProbeMap* sProbeMapIter_Map; // nonref + mork_num sProbeMapIter_Seed; // iter's cached copy of map's seed + + mork_i4 sProbeMapIter_HereIx; + + mork_change sProbeMapIter_Change; // morkMapIter API simulation dummy + mork_u1 sProbeMapIter_Pad[ 3 ]; // for u4 alignment + +public: + morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); + void CloseMapIter(morkEnv* ev); + + morkProbeMapIter( ); // zero most slots; caller must call InitProbeMapIter() + +protected: // protected so subclasses must provide suitable typesafe inlines: + + void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap); + + void InitMapIter(morkEnv* ev, morkProbeMap* ioMap) // morkMapIter compatibility + { this->InitProbeMapIter(ev, ioMap); } + + mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal); + mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal); + mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal); + + // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values. + // (Note the implied assumption that zero is never a good value pattern.) + + void* IterFirstVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; } + + void* IterNextVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; } + + void* IterHereVal(morkEnv* ev, void* outKey); + // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; } + + // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values. + // (Note the implied assumption that zero is never a good key pattern.) + + void* IterFirstKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; } + + void* IterNextKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; } + + void* IterHereKey(morkEnv* ev); + // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; } + +public: // simulation of the morkMapIter API for morkMap compatibility: + mork_change* First(morkEnv* ev, void* outKey, void* outVal); + mork_change* Next(morkEnv* ev, void* outKey, void* outVal); + mork_change* Here(morkEnv* ev, void* outKey, void* outVal); + + mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKPROBEMAP_ */ diff --git a/db/mork/src/morkQuickSort.cpp b/db/mork/src/morkQuickSort.cpp new file mode 100644 index 0000000000..c0a4c11d0a --- /dev/null +++ b/db/mork/src/morkQuickSort.cpp @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* We need this because Solaris' version of qsort is broken and + * causes array bounds reads. + */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKQUICKSORT_ +#include "morkQuickSort.h" +#endif + +#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc)) +# ifndef INLINE +# define INLINE inline +# endif +#else +# define INLINE +#endif + +static INLINE mork_u1* +morkQS_med3(mork_u1 *, mork_u1 *, mork_u1 *, mdbAny_Order, void *); + +static INLINE void +morkQS_swapfunc(mork_u1 *, mork_u1 *, int, int); + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define morkQS_swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define morkQS_SwapInit(a, es) swaptype = (a - (mork_u1 *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static INLINE void +morkQS_swapfunc(mork_u1* a, mork_u1* b, int n, int swaptype) +{ + if(swaptype <= 1) + morkQS_swapcode(long, a, b, n) + else + morkQS_swapcode(mork_u1, a, b, n) +} + +#define morkQS_swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + morkQS_swapfunc(a, b, (int)inSize, swaptype) + +#define morkQS_vecswap(a, b, n) if ((n) > 0) morkQS_swapfunc(a, b, (int)n, swaptype) + +static INLINE mork_u1 * +morkQS_med3(mork_u1* a, mork_u1* b, mork_u1* c, mdbAny_Order cmp, void* closure) +{ + return (*cmp)(a, b, closure) < 0 ? + ((*cmp)(b, c, closure) < 0 ? b : ((*cmp)(a, c, closure) < 0 ? c : a )) + :((*cmp)(b, c, closure) > 0 ? b : ((*cmp)(a, c, closure) < 0 ? a : c )); +} + +#define morkQS_MIN(x,y) ((x)<(y)?(x):(y)) + +void +morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize, + mdbAny_Order inOrder, void* ioClosure) +{ + mork_u1* pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + +tailCall: morkQS_SwapInit(ioVec, inSize); + swap_cnt = 0; + if (inCount < 7) { + for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize) + for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0; + pl -= inSize) + morkQS_swap(pl, pl - inSize); + return; + } + pm = ioVec + (inCount / 2) * inSize; + if (inCount > 7) { + pl = ioVec; + pn = ioVec + (inCount - 1) * inSize; + if (inCount > 40) { + d = (inCount / 8) * inSize; + pl = morkQS_med3(pl, pl + d, pl + 2 * d, inOrder, ioClosure); + pm = morkQS_med3(pm - d, pm, pm + d, inOrder, ioClosure); + pn = morkQS_med3(pn - 2 * d, pn - d, pn, inOrder, ioClosure); + } + pm = morkQS_med3(pl, pm, pn, inOrder, ioClosure); + } + morkQS_swap(ioVec, pm); + pa = pb = ioVec + inSize; + + pc = pd = ioVec + (inCount - 1) * inSize; + for (;;) { + while (pb <= pc && (r = (*inOrder)(pb, ioVec, ioClosure)) <= 0) { + if (r == 0) { + swap_cnt = 1; + morkQS_swap(pa, pb); + pa += inSize; + } + pb += inSize; + } + while (pb <= pc && (r = (*inOrder)(pc, ioVec, ioClosure)) >= 0) { + if (r == 0) { + swap_cnt = 1; + morkQS_swap(pc, pd); + pd -= inSize; + } + pc -= inSize; + } + if (pb > pc) + break; + morkQS_swap(pb, pc); + swap_cnt = 1; + pb += inSize; + pc -= inSize; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize) + for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0; + pl -= inSize) + morkQS_swap(pl, pl - inSize); + return; + } + + pn = ioVec + inCount * inSize; + r = morkQS_MIN(pa - ioVec, pb - pa); + morkQS_vecswap(ioVec, pb - r, r); + r = morkQS_MIN(pd - pc, (int)(pn - pd - inSize)); + morkQS_vecswap(pb, pn - r, r); + if ((r = pb - pa) > (int)inSize) + morkQuickSort(ioVec, r / inSize, inSize, inOrder, ioClosure); + if ((r = pd - pc) > (int)inSize) { + /* Iterate rather than recurse to save stack space */ + ioVec = pn - r; + inCount = r / inSize; + goto tailCall; + } +/* morkQuickSort(pn - r, r / inSize, inSize, inOrder, ioClosure);*/ +} + diff --git a/db/mork/src/morkQuickSort.h b/db/mork/src/morkQuickSort.h new file mode 100644 index 0000000000..a9c2e5546b --- /dev/null +++ b/db/mork/src/morkQuickSort.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKQUICKSORT_ +#define _MORKQUICKSORT_ 1 + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +extern void +morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize, + mdbAny_Order inOrder, void* ioClosure); + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKQUICKSORT_ */ diff --git a/db/mork/src/morkRow.cpp b/db/mork/src/morkRow.cpp new file mode 100644 index 0000000000..ede1bf646f --- /dev/null +++ b/db/mork/src/morkRow.cpp @@ -0,0 +1,933 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +#ifndef _MORKCELLOBJECT_ +#include "morkCellObject.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +#include "morkRowCellCursor.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +// notifications regarding row changes: + +void morkRow::NoteRowAddCol(morkEnv* ev, mork_column inColumn) +{ + if ( !this->IsRowRewrite() ) + { + mork_delta newDelta; + morkDelta_Init(newDelta, inColumn, morkChange_kAdd); + + if ( newDelta != mRow_Delta ) // not repeating existing data? + { + if ( this->HasRowDelta() ) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kAdd); + } + } + else + this->ClearRowDelta(); +} + +void morkRow::NoteRowCutCol(morkEnv* ev, mork_column inColumn) +{ + if ( !this->IsRowRewrite() ) + { + mork_delta newDelta; + morkDelta_Init(newDelta, inColumn, morkChange_kCut); + + if ( newDelta != mRow_Delta ) // not repeating existing data? + { + if ( this->HasRowDelta() ) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kCut); + } + } + else + this->ClearRowDelta(); +} + +void morkRow::NoteRowSetCol(morkEnv* ev, mork_column inColumn) +{ + if ( !this->IsRowRewrite() ) + { + if ( this->HasRowDelta() ) // already have one change recorded? + this->SetRowRewrite(); // just plan to write all row cells + else + this->SetRowDelta(inColumn, morkChange_kSet); + } + else + this->ClearRowDelta(); +} + +void morkRow::NoteRowSetAll(morkEnv* ev) +{ + this->SetRowRewrite(); // just plan to write all row cells + this->ClearRowDelta(); +} + +mork_u2 +morkRow::AddRowGcUse(morkEnv* ev) +{ + if ( this->IsRow() ) + { + if ( mRow_GcUses < morkRow_kMaxGcUses ) // not already maxed out? + ++mRow_GcUses; + } + else + this->NonRowTypeError(ev); + + return mRow_GcUses; +} + +mork_u2 +morkRow::CutRowGcUse(morkEnv* ev) +{ + if ( this->IsRow() ) + { + if ( mRow_GcUses ) // any outstanding uses to cut? + { + if ( mRow_GcUses < morkRow_kMaxGcUses ) // not frozen at max? + --mRow_GcUses; + } + else + this->GcUsesUnderflowWarning(ev); + } + else + this->NonRowTypeError(ev); + + return mRow_GcUses; +} + +/*static*/ void +morkRow::GcUsesUnderflowWarning(morkEnv* ev) +{ + ev->NewWarning("mRow_GcUses underflow"); +} + + +/*static*/ void +morkRow::NonRowTypeError(morkEnv* ev) +{ + ev->NewError("non morkRow"); +} + +/*static*/ void +morkRow::NonRowTypeWarning(morkEnv* ev) +{ + ev->NewWarning("non morkRow"); +} + +/*static*/ void +morkRow::LengthBeyondMaxError(morkEnv* ev) +{ + ev->NewError("mRow_Length over max"); +} + +/*static*/ void +morkRow::ZeroColumnError(morkEnv* ev) +{ + ev->NewError(" zero mork_column"); +} + +/*static*/ void +morkRow::NilCellsError(morkEnv* ev) +{ + ev->NewError("nil mRow_Cells"); +} + +void +morkRow::InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace, + mork_size inLength, morkPool* ioPool) + // if inLength is nonzero, cells will be allocated from ioPool +{ + if ( ioSpace && ioPool && inOid ) + { + if ( inLength <= morkRow_kMaxLength ) + { + if ( inOid->mOid_Id != morkRow_kMinusOneRid ) + { + mRow_Space = ioSpace; + mRow_Object = 0; + mRow_Cells = 0; + mRow_Oid = *inOid; + + mRow_Length = (mork_u2) inLength; + mRow_Seed = (mork_u2) (mork_ip) this; // "random" assignment + + mRow_GcUses = 0; + mRow_Pad = 0; + mRow_Flags = 0; + mRow_Tag = morkRow_kTag; + + morkZone* zone = &ioSpace->mSpace_Store->mStore_Zone; + + if ( inLength ) + mRow_Cells = ioPool->NewCells(ev, inLength, zone); + + if ( this->MaybeDirtySpaceStoreAndRow() ) // new row might dirty store + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + } + else + ioSpace->MinusOneRidError(ev); + } + else + this->LengthBeyondMaxError(ev); + } + else + ev->NilPointerError(); +} + +morkRowObject* +morkRow::AcquireRowObject(morkEnv* ev, morkStore* ioStore) +{ + morkRowObject* ro = mRow_Object; + if ( ro ) // need new row object? + ro->AddRef(); + else + { + nsIMdbHeap* heap = ioStore->mPort_Heap; + ro = new(*heap, ev) + morkRowObject(ev, morkUsage::kHeap, heap, this, ioStore); + if ( !ro ) + return (morkRowObject*) 0; + + morkRowObject::SlotWeakRowObject(ro, ev, &mRow_Object); + ro->AddRef(); + } + return ro; +} + +nsIMdbRow* +morkRow::AcquireRowHandle(morkEnv* ev, morkStore* ioStore) +{ + return AcquireRowObject(ev, ioStore); +} + +nsIMdbCell* +morkRow::AcquireCellHandle(morkEnv* ev, morkCell* ioCell, + mdb_column inCol, mork_pos inPos) +{ + nsIMdbHeap* heap = ev->mEnv_Heap; + morkCellObject* cellObj = new(*heap, ev) + morkCellObject(ev, morkUsage::kHeap, heap, this, ioCell, inCol, inPos); + if ( cellObj ) + { + nsIMdbCell* cellHandle = cellObj->AcquireCellHandle(ev); +// cellObj->CutStrongRef(ev->AsMdbEnv()); + return cellHandle; + } + return (nsIMdbCell*) 0; +} + +mork_count +morkRow::CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill) + // Count cells in ioVector that change existing cells in this row when + // ioVector is added to the row (as in TakeCells()). This is the set + // of cells with the same columns in ioVector and mRow_Cells, which do + // not have exactly the same value in mCell_Atom, and which do not both + // have change status equal to morkChange_kCut (because cutting a cut + // cell still yields a cell that has been cut). CountOverlap() also + // modifies the change attribute of any cell in ioVector to kDup when + // the change was previously kCut and the same column cell was found + // in this row with change also equal to kCut; this tells callers later + // they need not look for that cell in the row again on a second pass. +{ + mork_count outCount = 0; + mork_pos pos = 0; // needed by GetCell() + morkCell* cells = ioVector; + morkCell* end = cells + inFill; + --cells; // prepare for preincrement + while ( ++cells < end && ev->Good() ) + { + mork_column col = cells->GetColumn(); + + morkCell* old = this->GetCell(ev, col, &pos); + if ( old ) // same column? + { + mork_change newChg = cells->GetChange(); + mork_change oldChg = old->GetChange(); + if ( newChg != morkChange_kCut || oldChg != newChg ) // not cut+cut? + { + if ( cells->mCell_Atom != old->mCell_Atom ) // not same atom? + ++outCount; // cells will replace old significantly when added + } + else + cells->SetColumnAndChange(col, morkChange_kDup); // note dup status + } + } + return outCount; +} + +void +morkRow::MergeCells(morkEnv* ev, morkCell* ioVector, + mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap) + // MergeCells() is the part of TakeCells() that does the insertion. + // inOldRowFill is the old value of mRow_Length, and inOverlap is the + // number of cells in the intersection that must be updated. +{ + morkCell* newCells = mRow_Cells + inOldRowFill; // 1st new cell in row + morkCell* newEnd = newCells + mRow_Length; // one past last cell + + morkCell* srcCells = ioVector; + morkCell* srcEnd = srcCells + inVecLength; + + --srcCells; // prepare for preincrement + while ( ++srcCells < srcEnd && ev->Good() ) + { + mork_change srcChg = srcCells->GetChange(); + if ( srcChg != morkChange_kDup ) // anything to be done? + { + morkCell* dstCell = 0; + if ( inOverlap ) + { + mork_pos pos = 0; // needed by GetCell() + dstCell = this->GetCell(ev, srcCells->GetColumn(), &pos); + } + if ( dstCell ) + { + --inOverlap; // one fewer intersections to resolve + // swap the atoms in the cells to avoid ref counting here: + morkAtom* dstAtom = dstCell->mCell_Atom; + *dstCell = *srcCells; // bitwise copy, taking src atom + srcCells->mCell_Atom = dstAtom; // forget cell ref, if any + } + else if ( newCells < newEnd ) // another new cell exists? + { + dstCell = newCells++; // alloc another new cell + // take atom from source cell, transferring ref to this row: + *dstCell = *srcCells; // bitwise copy, taking src atom + srcCells->mCell_Atom = 0; // forget cell ref, if any + } + else // oops, we ran out... + ev->NewError("out of new cells"); + } + } +} + +void +morkRow::TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + morkStore* ioStore) +{ + if ( ioVector && inVecLength && ev->Good() ) + { + ++mRow_Seed; // intend to change structure of mRow_Cells + mork_size length = (mork_size) mRow_Length; + + mork_count overlap = this->CountOverlap(ev, ioVector, inVecLength); + + mork_size growth = inVecLength - overlap; // cells to add + mork_size newLength = length + growth; + + if ( growth && ev->Good() ) // need to add any cells? + { + morkZone* zone = &ioStore->mStore_Zone; + morkPool* pool = ioStore->StorePool(); + if ( !pool->AddRowCells(ev, this, length + growth, zone) ) + ev->NewError("cannot take cells"); + } + if ( ev->Good() ) + { + if ( mRow_Length >= newLength ) + this->MergeCells(ev, ioVector, inVecLength, length, overlap); + else + ev->NewError("not enough new cells"); + } + } +} + +mork_bool morkRow::MaybeDirtySpaceStoreAndRow() +{ + morkRowSpace* rowSpace = mRow_Space; + if ( rowSpace ) + { + morkStore* store = rowSpace->mSpace_Store; + if ( store && store->mStore_CanDirty ) + { + store->SetStoreDirty(); + rowSpace->mSpace_CanDirty = morkBool_kTrue; + } + + if ( rowSpace->mSpace_CanDirty ) + { + this->SetRowDirty(); + rowSpace->SetRowSpaceDirty(); + return morkBool_kTrue; + } + } + return morkBool_kFalse; +} + +morkCell* +morkRow::NewCell(morkEnv* ev, mdb_column inColumn, + mork_pos* outPos, morkStore* ioStore) +{ + ++mRow_Seed; // intend to change structure of mRow_Cells + mork_size length = (mork_size) mRow_Length; + *outPos = (mork_pos) length; + morkPool* pool = ioStore->StorePool(); + morkZone* zone = &ioStore->mStore_Zone; + + mork_bool canDirty = this->MaybeDirtySpaceStoreAndRow(); + + if ( pool->AddRowCells(ev, this, length + 1, zone) ) + { + morkCell* cell = mRow_Cells + length; + // next line equivalent to inline morkCell::SetCellDirty(): + if ( canDirty ) + cell->SetCellColumnDirty(inColumn); + else + cell->SetCellColumnClean(inColumn); + + if ( canDirty && !this->IsRowRewrite() ) + this->NoteRowAddCol(ev, inColumn); + + return cell; + } + + return (morkCell*) 0; +} + + + +void morkRow::SeekColumn(morkEnv* ev, mdb_pos inPos, + mdb_column* outColumn, mdbYarn* outYarn) +{ + morkCell* cells = mRow_Cells; + if ( cells && inPos < mRow_Length && inPos >= 0 ) + { + morkCell* c = cells + inPos; + if ( outColumn ) + *outColumn = c->GetColumn(); + if ( outYarn ) + c->mCell_Atom->GetYarn(outYarn); // nil atom works okay here + } + else + { + if ( outColumn ) + *outColumn = 0; + if ( outYarn ) + ((morkAtom*) 0)->GetYarn(outYarn); // yes this will work + } +} + +void +morkRow::NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn) +{ + morkCell* cells = mRow_Cells; + if ( cells ) + { + mork_column last = 0; + mork_column inCol = *ioColumn; + morkCell* end = cells + mRow_Length; + while ( cells < end ) + { + if ( inCol == last ) // found column? + { + if ( outYarn ) + cells->mCell_Atom->GetYarn(outYarn); // nil atom works okay here + *ioColumn = cells->GetColumn(); + return; // stop, we are done + } + else + { + last = cells->GetColumn(); + ++cells; + } + } + } + *ioColumn = 0; + if ( outYarn ) + ((morkAtom*) 0)->GetYarn(outYarn); // yes this will work +} + +morkCell* +morkRow::CellAt(morkEnv* ev, mork_pos inPos) const +{ + MORK_USED_1(ev); + morkCell* cells = mRow_Cells; + if ( cells && inPos < mRow_Length && inPos >= 0 ) + { + return cells + inPos; + } + return (morkCell*) 0; +} + +morkCell* +morkRow::GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const +{ + MORK_USED_1(ev); + morkCell* cells = mRow_Cells; + if ( cells ) + { + morkCell* end = cells + mRow_Length; + while ( cells < end ) + { + mork_column col = cells->GetColumn(); + if ( col == inColumn ) // found the desired column? + { + *outPos = cells - mRow_Cells; + return cells; + } + else + ++cells; + } + } + *outPos = -1; + return (morkCell*) 0; +} + +mork_aid +morkRow::GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const + // GetCellAtomAid() finds the cell with column inColumn, and sees if the + // atom has a token ID, and returns the atom's ID if there is one. Or + // else zero is returned if there is no such column, or no atom, or if + // the atom has no ID to return. This method is intended to support + // efficient updating of column indexes for rows in a row space. +{ + if (this->IsRow()) + { + morkCell* cells = mRow_Cells; + if ( cells ) + { + morkCell* end = cells + mRow_Length; + while ( cells < end ) + { + mork_column col = cells->GetColumn(); + if ( col == inColumn ) // found desired column? + { + morkAtom* atom = cells->mCell_Atom; + if ( atom && atom->IsBook() ) + return ((morkBookAtom*) atom)->mBookAtom_Id; + else + return 0; + } + else + ++cells; + } + } + } + else + this->NonRowTypeError(ev); + + return 0; +} + +void +morkRow::EmptyAllCells(morkEnv* ev) +{ + morkCell* cells = mRow_Cells; + if ( cells ) + { + morkStore* store = this->GetRowSpaceStore(ev); + if ( store ) + { + if ( this->MaybeDirtySpaceStoreAndRow() ) + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkPool* pool = store->StorePool(); + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end ) + { + if ( cells->mCell_Atom ) + cells->SetAtom(ev, (morkAtom*) 0, pool); + } + } + } +} + +void +morkRow::cut_all_index_entries(morkEnv* ev) +{ + morkRowSpace* rowSpace = mRow_Space; + if ( rowSpace->mRowSpace_IndexCount ) // any indexes? + { + morkCell* cells = mRow_Cells; + if ( cells ) + { + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end ) + { + morkAtom* atom = cells->mCell_Atom; + if ( atom ) + { + mork_aid atomAid = atom->GetBookAtomAid(); + if ( atomAid ) + { + mork_column col = cells->GetColumn(); + morkAtomRowMap* map = rowSpace->FindMap(ev, col); + if ( map ) // cut row from index for this column? + map->CutAid(ev, atomAid); + } + } + } + } + } +} + +void +morkRow::CutAllColumns(morkEnv* ev) +{ + morkStore* store = this->GetRowSpaceStore(ev); + if ( store ) + { + if ( this->MaybeDirtySpaceStoreAndRow() ) + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkRowSpace* rowSpace = mRow_Space; + if ( rowSpace->mRowSpace_IndexCount ) // any indexes? + this->cut_all_index_entries(ev); + + morkPool* pool = store->StorePool(); + pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone); + } +} + +void +morkRow::SetRow(morkEnv* ev, const morkRow* inSourceRow) +{ + // note inSourceRow might be in another DB, with a different store... + morkStore* store = this->GetRowSpaceStore(ev); + morkStore* srcStore = inSourceRow->GetRowSpaceStore(ev); + if ( store && srcStore ) + { + if ( this->MaybeDirtySpaceStoreAndRow() ) + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkRowSpace* rowSpace = mRow_Space; + mork_count indexes = rowSpace->mRowSpace_IndexCount; // any indexes? + + mork_bool sameStore = ( store == srcStore ); // identical stores? + morkPool* pool = store->StorePool(); + if ( pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone) ) + { + mork_fill fill = inSourceRow->mRow_Length; + if ( pool->AddRowCells(ev, this, fill, &store->mStore_Zone) ) + { + morkCell* dst = mRow_Cells; + morkCell* dstEnd = dst + mRow_Length; + + const morkCell* src = inSourceRow->mRow_Cells; + const morkCell* srcEnd = src + fill; + --dst; --src; // prepare both for preincrement: + + while ( ++dst < dstEnd && ++src < srcEnd && ev->Good() ) + { + morkAtom* atom = src->mCell_Atom; + mork_column dstCol = src->GetColumn(); + // Note we modify the mCell_Atom slot directly instead of using + // morkCell::SetAtom(), because we know it starts equal to nil. + + if ( sameStore ) // source and dest in same store? + { + // next line equivalent to inline morkCell::SetCellDirty(): + dst->SetCellColumnDirty(dstCol); + dst->mCell_Atom = atom; + if ( atom ) // another ref to non-nil atom? + atom->AddCellUse(ev); + } + else // need to dup items from src store in a dest store + { + dstCol = store->CopyToken(ev, dstCol, srcStore); + if ( dstCol ) + { + // next line equivalent to inline morkCell::SetCellDirty(): + dst->SetCellColumnDirty(dstCol); + atom = store->CopyAtom(ev, atom); + dst->mCell_Atom = atom; + if ( atom ) // another ref? + atom->AddCellUse(ev); + } + } + if ( indexes && atom ) + { + mork_aid atomAid = atom->GetBookAtomAid(); + if ( atomAid ) + { + morkAtomRowMap* map = rowSpace->FindMap(ev, dstCol); + if ( map ) + map->AddAid(ev, atomAid, this); + } + } + } + } + } + } +} + +void +morkRow::AddRow(morkEnv* ev, const morkRow* inSourceRow) +{ + if ( mRow_Length ) // any existing cells we might need to keep? + { + ev->StubMethodOnlyError(); + } + else + this->SetRow(ev, inSourceRow); // just exactly duplicate inSourceRow +} + +void +morkRow::OnZeroRowGcUse(morkEnv* ev) +// OnZeroRowGcUse() is called when CutRowGcUse() returns zero. +{ + MORK_USED_1(ev); + // ev->NewWarning("need to implement OnZeroRowGcUse"); +} + +void +morkRow::DirtyAllRowContent(morkEnv* ev) +{ + MORK_USED_1(ev); + + if ( this->MaybeDirtySpaceStoreAndRow() ) + { + this->SetRowRewrite(); + this->NoteRowSetAll(ev); + } + morkCell* cells = mRow_Cells; + if ( cells ) + { + morkCell* end = cells + mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end ) + { + cells->SetCellDirty(); + } + } +} + +morkStore* +morkRow::GetRowSpaceStore(morkEnv* ev) const +{ + morkRowSpace* rowSpace = mRow_Space; + if ( rowSpace ) + { + morkStore* store = rowSpace->mSpace_Store; + if ( store ) + { + if ( store->IsStore() ) + { + return store; + } + else + store->NonStoreTypeError(ev); + } + else + ev->NilPointerError(); + } + else + ev->NilPointerError(); + + return (morkStore*) 0; +} + +void morkRow::CutColumn(morkEnv* ev, mdb_column inColumn) +{ + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + if ( cell ) + { + morkStore* store = this->GetRowSpaceStore(ev); + if ( store ) + { + if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() ) + this->NoteRowCutCol(ev, inColumn); + + morkRowSpace* rowSpace = mRow_Space; + morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )? + rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0; + if ( map ) // this row attribute is indexed by row space? + { + morkAtom* oldAtom = cell->mCell_Atom; + if ( oldAtom ) // need to cut an entry from the index? + { + mork_aid oldAid = oldAtom->GetBookAtomAid(); + if ( oldAid ) // cut old row attribute from row index in space? + map->CutAid(ev, oldAid); + } + } + + morkPool* pool = store->StorePool(); + cell->SetAtom(ev, (morkAtom*) 0, pool); + + mork_fill fill = mRow_Length; // should not be zero + MORK_ASSERT(fill); + if ( fill ) // index < fill for last cell exists? + { + mork_fill last = fill - 1; // index of last cell in row + + if ( pos < (mork_pos)last ) // need to move cells following cut cell? + { + morkCell* lastCell = mRow_Cells + last; + mork_count after = last - pos; // cell count after cut cell + morkCell* next = cell + 1; // next cell after cut cell + MORK_MEMMOVE(cell, next, after * sizeof(morkCell)); + lastCell->SetColumnAndChange(0, 0); + lastCell->mCell_Atom = 0; + } + + if ( ev->Good() ) + pool->CutRowCells(ev, this, fill - 1, &store->mStore_Zone); + } + } + } +} + +morkAtom* morkRow::GetColumnAtom(morkEnv* ev, mdb_column inColumn) +{ + if ( ev->Good() ) + { + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + if ( cell ) + return cell->mCell_Atom; + } + return (morkAtom*) 0; +} + +void morkRow::AddColumn(morkEnv* ev, mdb_column inColumn, + const mdbYarn* inYarn, morkStore* ioStore) +{ + if ( ev->Good() ) + { + mork_pos pos = -1; + morkCell* cell = this->GetCell(ev, inColumn, &pos); + morkCell* oldCell = cell; // need to know later whether new + if ( !cell ) // column does not yet exist? + cell = this->NewCell(ev, inColumn, &pos, ioStore); + + if ( cell ) + { + morkAtom* oldAtom = cell->mCell_Atom; + + morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */); + if ( atom && atom != oldAtom ) + { + morkRowSpace* rowSpace = mRow_Space; + morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )? + rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0; + + if ( map ) // inColumn is indexed by row space? + { + if ( oldAtom && oldAtom != atom ) // cut old cell from index? + { + mork_aid oldAid = oldAtom->GetBookAtomAid(); + if ( oldAid ) // cut old row attribute from row index in space? + map->CutAid(ev, oldAid); + } + } + + cell->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom + + if ( oldCell ) // we changed a pre-existing cell in the row? + { + ++mRow_Seed; + if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() ) + this->NoteRowAddCol(ev, inColumn); + } + + if ( map ) // inColumn is indexed by row space? + { + mork_aid newAid = atom->GetBookAtomAid(); + if ( newAid ) // add new row attribute to row index in space? + map->AddAid(ev, newAid, this); + } + } + } + } +} + +morkRowCellCursor* +morkRow::NewRowCellCursor(morkEnv* ev, mdb_pos inPos) +{ + morkRowCellCursor* outCursor = 0; + if ( ev->Good() ) + { + morkStore* store = this->GetRowSpaceStore(ev); + if ( store ) + { + morkRowObject* rowObj = this->AcquireRowObject(ev, store); + if ( rowObj ) + { + nsIMdbHeap* heap = store->mPort_Heap; + morkRowCellCursor* cursor = new(*heap, ev) + morkRowCellCursor(ev, morkUsage::kHeap, heap, rowObj); + + if ( cursor ) + { + if ( ev->Good() ) + { + cursor->mRowCellCursor_Col = inPos; + outCursor = cursor; + } + else + cursor->CutStrongRef(ev->mEnv_SelfAsMdbEnv); + } + rowObj->Release(); // always cut ref (cursor has its own) + } + } + } + return outCursor; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkRow.h b/db/mork/src/morkRow.h new file mode 100644 index 0000000000..31fa206095 --- /dev/null +++ b/db/mork/src/morkRow.h @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKROW_ +#define _MORKROW_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbRow; +class nsIMdbCell; +#define morkDerived_kRow /*i*/ 0x5277 /* ascii 'Rw' */ + +#define morkRow_kMaxGcUses 0x0FF /* max for 8-bit unsigned int */ +#define morkRow_kMaxLength 0x0FFFF /* max for 16-bit unsigned int */ +#define morkRow_kMinusOneRid ((mork_rid) -1) + +#define morkRow_kTag 'r' /* magic signature for mRow_Tag */ + +#define morkRow_kNotedBit ((mork_u1) (1 << 0)) /* space has change notes */ +#define morkRow_kRewriteBit ((mork_u1) (1 << 1)) /* must rewrite all cells */ +#define morkRow_kDirtyBit ((mork_u1) (1 << 2)) /* row has been changed */ + +class morkRow{ // row of cells + +public: // state is public because the entire Mork system is private + + morkRowSpace* mRow_Space; // mRow_Space->SpaceScope() is the row scope + morkRowObject* mRow_Object; // refcount & other state for object sharing + morkCell* mRow_Cells; + mdbOid mRow_Oid; + + mork_delta mRow_Delta; // space to note a single column change + + mork_u2 mRow_Length; // physical count of cells in mRow_Cells + mork_u2 mRow_Seed; // count changes in mRow_Cells structure + + mork_u1 mRow_GcUses; // persistent references from tables + mork_u1 mRow_Pad; // for u1 alignment + mork_u1 mRow_Flags; // one-byte flags slot + mork_u1 mRow_Tag; // one-byte tag (need u4 alignment pad) + +public: // interpreting mRow_Delta + + mork_bool HasRowDelta() const { return ( mRow_Delta != 0 ); } + + void ClearRowDelta() { mRow_Delta = 0; } + + void SetRowDelta(mork_column inCol, mork_change inChange) + { morkDelta_Init(mRow_Delta, inCol, inChange); } + + mork_column GetDeltaColumn() const { return morkDelta_Column(mRow_Delta); } + mork_change GetDeltaChange() const { return morkDelta_Change(mRow_Delta); } + +public: // noting row changes + + void NoteRowSetAll(morkEnv* ev); + void NoteRowSetCol(morkEnv* ev, mork_column inCol); + void NoteRowAddCol(morkEnv* ev, mork_column inCol); + void NoteRowCutCol(morkEnv* ev, mork_column inCol); + +public: // flags bit twiddling + + void SetRowNoted() { mRow_Flags |= morkRow_kNotedBit; } + void SetRowRewrite() { mRow_Flags |= morkRow_kRewriteBit; } + void SetRowDirty() { mRow_Flags |= morkRow_kDirtyBit; } + + void ClearRowNoted() { mRow_Flags &= (mork_u1) ~morkRow_kNotedBit; } + void ClearRowRewrite() { mRow_Flags &= (mork_u1) ~morkRow_kRewriteBit; } + void SetRowClean() { mRow_Flags = 0; mRow_Delta = 0; } + + mork_bool IsRowNoted() const + { return ( mRow_Flags & morkRow_kNotedBit ) != 0; } + + mork_bool IsRowRewrite() const + { return ( mRow_Flags & morkRow_kRewriteBit ) != 0; } + + mork_bool IsRowClean() const + { return ( mRow_Flags & morkRow_kDirtyBit ) == 0; } + + mork_bool IsRowDirty() const + { return ( mRow_Flags & morkRow_kDirtyBit ) != 0; } + + mork_bool IsRowUsed() const + { return mRow_GcUses != 0; } + +public: // other row methods + morkRow( ) { } + morkRow(const mdbOid* inOid) :mRow_Oid(*inOid) { } + void InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace, + mork_size inLength, morkPool* ioPool); + // if inLength is nonzero, cells will be allocated from ioPool + + morkRowObject* AcquireRowObject(morkEnv* ev, morkStore* ioStore); + nsIMdbRow* AcquireRowHandle(morkEnv* ev, morkStore* ioStore); + nsIMdbCell* AcquireCellHandle(morkEnv* ev, morkCell* ioCell, + mdb_column inColumn, mork_pos inPos); + + mork_u2 AddRowGcUse(morkEnv* ev); + mork_u2 CutRowGcUse(morkEnv* ev); + + + mork_bool MaybeDirtySpaceStoreAndRow(); + +public: // internal row methods + + void cut_all_index_entries(morkEnv* ev); + + // void cut_cell_from_space_index(morkEnv* ev, morkCell* ioCell); + + mork_count CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill); + // Count cells in ioVector that change existing cells in this row when + // ioVector is added to the row (as in TakeCells()). This is the set + // of cells with the same columns in ioVector and mRow_Cells, which do + // not have exactly the same value in mCell_Atom, and which do not both + // have change status equal to morkChange_kCut (because cutting a cut + // cell still yields a cell that has been cut). CountOverlap() also + // modifies the change attribute of any cell in ioVector to kDup when + // the change was previously kCut and the same column cell was found + // in this row with change also equal to kCut; this tells callers later + // they need not look for that cell in the row again on a second pass. + + void MergeCells(morkEnv* ev, morkCell* ioVector, + mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap); + // MergeCells() is the part of TakeCells() that does the insertion. + // inOldRowFill is the old value of mRow_Length, and inOverlap is the + // number of cells in the intersection that must be updated. + + void TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength, + morkStore* ioStore); + + morkCell* NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos, + morkStore* ioStore); + morkCell* GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const; + morkCell* CellAt(morkEnv* ev, mork_pos inPos) const; + + mork_aid GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const; + // GetCellAtomAid() finds the cell with column inColumn, and sees if the + // atom has a token ID, and returns the atom's ID if there is one. Or + // else zero is returned if there is no such column, or no atom, or if + // the atom has no ID to return. This method is intended to support + // efficient updating of column indexes for rows in a row space. + +public: // external row methods + + void DirtyAllRowContent(morkEnv* ev); + + morkStore* GetRowSpaceStore(morkEnv* ev) const; + + void AddColumn(morkEnv* ev, mdb_column inColumn, + const mdbYarn* inYarn, morkStore* ioStore); + + morkAtom* GetColumnAtom(morkEnv* ev, mdb_column inColumn); + + void NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn); + + void SeekColumn(morkEnv* ev, mdb_pos inPos, + mdb_column* outColumn, mdbYarn* outYarn); + + void CutColumn(morkEnv* ev, mdb_column inColumn); + + morkRowCellCursor* NewRowCellCursor(morkEnv* ev, mdb_pos inPos); + + void EmptyAllCells(morkEnv* ev); + void AddRow(morkEnv* ev, const morkRow* inSourceRow); + void SetRow(morkEnv* ev, const morkRow* inSourceRow); + void CutAllColumns(morkEnv* ev); + + void OnZeroRowGcUse(morkEnv* ev); + // OnZeroRowGcUse() is called when CutRowGcUse() returns zero. + +public: // dynamic typing + + mork_bool IsRow() const { return mRow_Tag == morkRow_kTag; } + +public: // hash and equal + + mork_u4 HashRow() const + { + return (mRow_Oid.mOid_Scope << 16) ^ mRow_Oid.mOid_Id; + } + + mork_bool EqualRow(const morkRow* ioRow) const + { + return + ( + ( mRow_Oid.mOid_Scope == ioRow->mRow_Oid.mOid_Scope ) + && ( mRow_Oid.mOid_Id == ioRow->mRow_Oid.mOid_Id ) + ); + } + + mork_bool EqualOid(const mdbOid* ioOid) const + { + return + ( + ( mRow_Oid.mOid_Scope == ioOid->mOid_Scope ) + && ( mRow_Oid.mOid_Id == ioOid->mOid_Id ) + ); + } + +public: // errors + static void ZeroColumnError(morkEnv* ev); + static void LengthBeyondMaxError(morkEnv* ev); + static void NilCellsError(morkEnv* ev); + static void NonRowTypeError(morkEnv* ev); + static void NonRowTypeWarning(morkEnv* ev); + static void GcUsesUnderflowWarning(morkEnv* ev); + +private: // copying is not allowed + morkRow(const morkRow& other); + morkRow& operator=(const morkRow& other); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROW_ */ + diff --git a/db/mork/src/morkRowCellCursor.cpp b/db/mork/src/morkRowCellCursor.cpp new file mode 100644 index 0000000000..d5d4a0d203 --- /dev/null +++ b/db/mork/src/morkRowCellCursor.cpp @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +#include "morkRowCellCursor.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkRowCellCursor::CloseMorkNode(morkEnv* ev) // CloseRowCellCursor() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseRowCellCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowCellCursor::~morkRowCellCursor() // CloseRowCellCursor() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowCellCursor::morkRowCellCursor(morkEnv* ev, + const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRowObject* ioRowObject) +: morkCursor(ev, inUsage, ioHeap) +, mRowCellCursor_RowObject( 0 ) +, mRowCellCursor_Col( 0 ) +{ + if ( ev->Good() ) + { + if ( ioRowObject ) + { + morkRow* row = ioRowObject->mRowObject_Row; + if ( row ) + { + if ( row->IsRow() ) + { + mCursor_Pos = -1; + mCursor_Seed = row->mRow_Seed; + + morkRowObject::SlotStrongRowObject(ioRowObject, ev, + &mRowCellCursor_RowObject); + if ( ev->Good() ) + mNode_Derived = morkDerived_kRowCellCursor; + } + else + row->NonRowTypeError(ev); + } + else + ioRowObject->NilRowError(ev); + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkRowCellCursor, morkCursor, nsIMdbRowCellCursor) + +/*public non-poly*/ void +morkRowCellCursor::CloseRowCellCursor(morkEnv* ev) +{ + if ( this->IsNode() ) + { + mCursor_Pos = -1; + mCursor_Seed = 0; + morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev, + &mRowCellCursor_RowObject); + this->CloseCursor(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkRowCellCursor::NilRowObjectError(morkEnv* ev) +{ + ev->NewError("nil mRowCellCursor_RowObject"); +} + +/*static*/ void +morkRowCellCursor::NonRowCellCursorTypeError(morkEnv* ev) +{ + ev->NewError("non morkRowCellCursor"); +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkRowCellCursor::SetRow(nsIMdbEnv* mev, nsIMdbRow* ioRow) +{ + nsresult outErr = NS_OK; + morkRow* row = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + row = (morkRow *) ioRow; + morkStore* store = row->GetRowSpaceStore(ev); + if ( store ) + { + morkRowObject* rowObj = row->AcquireRowObject(ev, store); + if ( rowObj ) + { + morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev, + &mRowCellCursor_RowObject); + + mRowCellCursor_RowObject = rowObj; // take this strong ref + mCursor_Seed = row->mRow_Seed; + + row->GetCell(ev, mRowCellCursor_Col, &mCursor_Pos); + } + } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowCellCursor::GetRow(nsIMdbEnv* mev, nsIMdbRow** acqRow) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject* rowObj = mRowCellCursor_RowObject; + if ( rowObj ) + outRow = rowObj->AcquireRowHandle(ev); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin cell creation methods ----- +NS_IMETHODIMP +morkRowCellCursor::MakeCell( // get cell at current pos in the row + nsIMdbEnv* mev, // context + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos, // position of cell in row sequence + nsIMdbCell** acqCell) +{ + nsresult outErr = NS_OK; + nsIMdbCell* outCell = 0; + mdb_pos pos = 0; + mdb_column col = 0; + morkRow* row = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + pos = mCursor_Pos; + morkCell* cell = row->CellAt(ev, pos); + if ( cell ) + { + col = cell->GetColumn(); + outCell = row->AcquireCellHandle(ev, cell, col, pos); + } + outErr = ev->AsErr(); + } + if ( acqCell ) + *acqCell = outCell; + if ( outPos ) + *outPos = pos; + if ( outColumn ) + *outColumn = col; + + return outErr; +} +// } ----- end cell creation methods ----- + +// { ----- begin cell seeking methods ----- +NS_IMETHODIMP +morkRowCellCursor::SeekCell( // same as SetRow() followed by MakeCell() + nsIMdbEnv* mev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end cell seeking methods ----- + +// { ----- begin cell iteration methods ----- +NS_IMETHODIMP +morkRowCellCursor::NextCell( // get next cell in the row + nsIMdbEnv* mev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) +{ + morkEnv* ev = morkEnv::FromMdbEnv(mev); + mdb_column col = 0; + mdb_pos pos = mRowCellCursor_Col; + if ( pos < 0 ) + pos = 0; + else + ++pos; + + morkCell* cell = mRowCellCursor_RowObject->mRowObject_Row->CellAt(ev, pos); + if ( cell ) + { + col = cell->GetColumn(); + *acqCell = mRowCellCursor_RowObject->mRowObject_Row->AcquireCellHandle(ev, cell, col, pos); + } + else + { + *acqCell = nullptr; + pos = -1; + } + if ( outPos ) + *outPos = pos; + if ( outColumn ) + *outColumn = col; + + mRowCellCursor_Col = pos; + return NS_OK; +} + +NS_IMETHODIMP +morkRowCellCursor::PickNextCell( // get next cell in row within filter set + nsIMdbEnv* mev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) +// Note that inFilterSet should not have too many (many more than 10?) +// cols, since this might imply a potential excessive consumption of time +// over many cursor calls when looking for column and filter intersection. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// } ----- end cell iteration methods ----- + +// } ===== end nsIMdbRowCellCursor methods ===== + diff --git a/db/mork/src/morkRowCellCursor.h b/db/mork/src/morkRowCellCursor.h new file mode 100644 index 0000000000..c9d95a2f24 --- /dev/null +++ b/db/mork/src/morkRowCellCursor.h @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKROWCELLCURSOR_ +#define _MORKROWCELLCURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinRowCellCursor; +#define morkDerived_kRowCellCursor /*i*/ 0x6343 /* ascii 'cC' */ + +class morkRowCellCursor : public morkCursor, public nsIMdbRowCellCursor { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + +public: // state is public because the entire Mork system is private + + NS_DECL_ISUPPORTS_INHERITED + morkRowObject* mRowCellCursor_RowObject; // strong ref to row + mork_column mRowCellCursor_Col; // col of cell last at mCursor_Pos + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowCellCursor() + +public: // morkRowCellCursor construction & destruction + morkRowCellCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRowObject* ioRowObject); + void CloseRowCellCursor(morkEnv* ev); // called by CloseMorkNode(); + + // { ----- begin attribute methods ----- + NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) override; // sets pos to -1 + NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) override; + // } ----- end attribute methods ----- + + // { ----- begin cell creation methods ----- + NS_IMETHOD MakeCell( // get cell at current pos in the row + nsIMdbEnv* ev, // context + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos, // position of cell in row sequence + nsIMdbCell** acqCell) override; // the cell at inPos + // } ----- end cell creation methods ----- + + // { ----- begin cell seeking methods ----- + NS_IMETHOD SeekCell( // same as SetRow() followed by MakeCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + nsIMdbCell** acqCell) override; // the cell at inPos + // } ----- end cell seeking methods ----- + + // { ----- begin cell iteration methods ----- + NS_IMETHOD NextCell( // get next cell in the row + nsIMdbEnv* ev, // context + nsIMdbCell** acqCell, // changes to the next cell in the iteration + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) override; // position of cell in row sequence + + NS_IMETHOD PickNextCell( // get next cell in row within filter set + nsIMdbEnv* ev, // context + nsIMdbCell* ioCell, // changes to the next cell in the iteration + const mdbColumnSet* inFilterSet, // col set of actual caller interest + mdb_column* outColumn, // column for this particular cell + mdb_pos* outPos) override; // position of cell in row sequence + + // Note that inFilterSet should not have too many (many more than 10?) + // cols, since this might imply a potential excessive consumption of time + // over many cursor calls when looking for column and filter intersection. + // } ----- end cell iteration methods ----- + + +private: // copying is not allowed + morkRowCellCursor(const morkRowCellCursor& other); + morkRowCellCursor& operator=(const morkRowCellCursor& other); + virtual ~morkRowCellCursor(); // assert that close executed earlier + +public: // dynamic type identification + mork_bool IsRowCellCursor() const + { return IsNode() && mNode_Derived == morkDerived_kRowCellCursor; } +// } ===== end morkNode methods ===== + +public: // errors + static void NilRowObjectError(morkEnv* ev); + static void NonRowCellCursorTypeError(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowCellCursor(morkRowCellCursor* me, + morkEnv* ev, morkRowCellCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongRowCellCursor(morkRowCellCursor* me, + morkEnv* ev, morkRowCellCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWCELLCURSOR_ */ diff --git a/db/mork/src/morkRowMap.cpp b/db/mork/src/morkRowMap.cpp new file mode 100644 index 0000000000..35c8b9202d --- /dev/null +++ b/db/mork/src/morkRowMap.cpp @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkRowMap::CloseMorkNode(morkEnv* ev) // CloseRowMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseRowMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowMap::~morkRowMap() // assert CloseRowMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowMap::morkRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots) +: morkMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0, + inSlots, ioSlotHeap, /*inHoldChanges*/ morkBool_kFalse) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kRowMap; +} + +/*public non-poly*/ void +morkRowMap::CloseRowMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CloseMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + + +// { ===== begin morkMap poly interface ===== +/*virtual*/ mork_bool // +morkRowMap::Equal(morkEnv* ev, const void* inKeyA, + const void* inKeyB) const +{ + MORK_USED_1(ev); + return (*(const morkRow**) inKeyA)->EqualRow(*(const morkRow**) inKeyB); +} + +/*virtual*/ mork_u4 // +morkRowMap::Hash(morkEnv* ev, const void* inKey) const +{ + MORK_USED_1(ev); + return (*(const morkRow**) inKey)->HashRow(); +} +// } ===== end morkMap poly interface ===== + + +mork_bool +morkRowMap::AddRow(morkEnv* ev, morkRow* ioRow) +{ + if ( ev->Good() ) + { + this->Put(ev, &ioRow, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0); + } + return ev->Good(); +} + +morkRow* +morkRowMap::CutOid(morkEnv* ev, const mdbOid* inOid) +{ + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->Cut(ev, &key, &oldKey, /*val*/ (void*) 0, + (mork_change**) 0); + + return oldKey; +} + +morkRow* +morkRowMap::CutRow(morkEnv* ev, const morkRow* ioRow) +{ + morkRow* oldKey = 0; + this->Cut(ev, &ioRow, &oldKey, /*val*/ (void*) 0, + (mork_change**) 0); + + return oldKey; +} + +morkRow* +morkRowMap::GetOid(morkEnv* ev, const mdbOid* inOid) +{ + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0); + + return oldKey; +} + +morkRow* +morkRowMap::GetRow(morkEnv* ev, const morkRow* ioRow) +{ + morkRow* oldKey = 0; + this->Get(ev, &ioRow, &oldKey, /*val*/ (void*) 0, (mork_change**) 0); + + return oldKey; +} + + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkRowProbeMap::CloseMorkNode(morkEnv* ev) // CloseRowProbeMap() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseRowProbeMap(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowProbeMap::~morkRowProbeMap() // assert CloseRowProbeMap() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowProbeMap::morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots) +: morkProbeMap(ev, inUsage, ioHeap, + /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0, + ioSlotHeap, inSlots, + /*inHoldChanges*/ morkBool_kTrue) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kRowProbeMap; +} + +/*public non-poly*/ void +morkRowProbeMap::CloseRowProbeMap(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + this->CloseProbeMap(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b) +morkRowProbeMap::MapTest(morkEnv* ev, const void* inMapKey, + const void* inAppKey) const +{ + MORK_USED_1(ev); + const morkRow* key = *(const morkRow**) inMapKey; + if ( key ) + { + mork_bool hit = key->EqualRow(*(const morkRow**) inAppKey); + return ( hit ) ? morkTest_kHit : morkTest_kMiss; + } + else + return morkTest_kVoid; +} + +/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b) +morkRowProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const +{ + const morkRow* key = *(const morkRow**) inAppKey; + if ( key ) + return key->HashRow(); + else + { + ev->NilPointerWarning(); + return 0; + } +} + +/*virtual*/ mork_u4 +morkRowProbeMap::ProbeMapHashMapKey(morkEnv* ev, + const void* inMapKey) const +{ + const morkRow* key = *(const morkRow**) inMapKey; + if ( key ) + return key->HashRow(); + else + { + ev->NilPointerWarning(); + return 0; + } +} + +mork_bool +morkRowProbeMap::AddRow(morkEnv* ev, morkRow* ioRow) +{ + if ( ev->Good() ) + { + this->MapAtPut(ev, &ioRow, /*val*/ (void*) 0, + /*key*/ (void*) 0, /*val*/ (void*) 0); + } + return ev->Good(); +} + +morkRow* +morkRowProbeMap::CutOid(morkEnv* ev, const mdbOid* inOid) +{ + MORK_USED_1(inOid); + morkProbeMap::ProbeMapCutError(ev); + + return 0; +} + +morkRow* +morkRowProbeMap::CutRow(morkEnv* ev, const morkRow* ioRow) +{ + MORK_USED_1(ioRow); + morkProbeMap::ProbeMapCutError(ev); + + return 0; +} + +morkRow* +morkRowProbeMap::GetOid(morkEnv* ev, const mdbOid* inOid) +{ + morkRow row(inOid); + morkRow* key = &row; + morkRow* oldKey = 0; + this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0); + + return oldKey; +} + +morkRow* +morkRowProbeMap::GetRow(morkEnv* ev, const morkRow* ioRow) +{ + morkRow* oldKey = 0; + this->MapAt(ev, &ioRow, &oldKey, /*val*/ (void*) 0); + + return oldKey; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkRowMap.h b/db/mork/src/morkRowMap.h new file mode 100644 index 0000000000..5bf2208296 --- /dev/null +++ b/db/mork/src/morkRowMap.h @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKROWMAP_ +#define _MORKROWMAP_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +#include "morkProbeMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowMap /*i*/ 0x724D /* ascii 'rM' */ + +/*| morkRowMap: maps a set of morkRow by contained Oid +|*/ +class morkRowMap : public morkMap { // for mapping row IDs to rows + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowMap() only if open + virtual ~morkRowMap(); // assert that CloseRowMap() executed earlier + +public: // morkMap construction & destruction + morkRowMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots); + void CloseRowMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsRowMap() const + { return IsNode() && mNode_Derived == morkDerived_kRowMap; } +// } ===== end morkNode methods ===== + +// { ===== begin morkMap poly interface ===== + virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b) + Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override; + // implemented using morkRow::EqualRow() + + virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b) + Hash(morkEnv* ev, const void* inKey) const override; + // implemented using morkRow::HashRow() +// } ===== end morkMap poly interface ===== + +public: // other map methods + + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); + // AddRow() returns ev->Good() + + morkRow* CutOid(morkEnv* ev, const mdbOid* inOid); + // CutRid() returns the row removed equal to inRid, if there was one + + morkRow* CutRow(morkEnv* ev, const morkRow* ioRow); + // CutRow() returns the row removed equal to ioRow, if there was one + + morkRow* GetOid(morkEnv* ev, const mdbOid* inOid); + // GetOid() returns the row equal to inRid, or else nil + + morkRow* GetRow(morkEnv* ev, const morkRow* ioRow); + // GetRow() returns the row equal to ioRow, or else nil + + // note the rows are owned elsewhere, usuall by morkRowSpace + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowMap(morkRowMap* me, + morkEnv* ev, morkRowMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongRowMap(morkRowMap* me, + morkEnv* ev, morkRowMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +class morkRowMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkRowMapIter(morkEnv* ev, morkRowMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkRowMapIter( ) : morkMapIter() { } + void InitRowMapIter(morkEnv* ev, morkRowMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr) + { return this->First(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr) + { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr) + { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr) + { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); } +}; + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowProbeMap /*i*/ 0x726D /* ascii 'rm' */ + +/*| morkRowProbeMap: maps a set of morkRow by contained Oid +|*/ +class morkRowProbeMap : public morkProbeMap { // for mapping row IDs to rows + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowProbeMap() only if open + virtual ~morkRowProbeMap(); // assert CloseRowProbeMap() executed earlier + +public: // morkMap construction & destruction + morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots); + void CloseRowProbeMap(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsRowMap() const + { return IsNode() && mNode_Derived == morkDerived_kRowMap; } +// } ===== end morkNode methods ===== + + // { ===== begin morkProbeMap methods ===== + virtual mork_test // hit(a,b) implies hash(a) == hash(b) + MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override; + + virtual mork_u4 // hit(a,b) implies hash(a) == hash(b) + MapHash(morkEnv* ev, const void* inAppKey) const override; + + virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override; + + // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey); + + // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map + // void* ioMapKey, mork_count inKeyCount); // array of keys inside map + + // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map + // const void* inAppKey, const void* inAppVal, // (key,val) outside map + // void* outMapKey, void* outMapVal); // (key,val) inside map + + // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map + // const void* inMapKey, const void* inMapVal, // (key,val) inside map + // void* outAppKey, void* outAppVal) const; // (key,val) outside map + // } ===== end morkProbeMap methods ===== + +public: // other map methods + + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); + // AddRow() returns ev->Good() + + morkRow* CutOid(morkEnv* ev, const mdbOid* inOid); + // CutRid() returns the row removed equal to inRid, if there was one + + morkRow* CutRow(morkEnv* ev, const morkRow* ioRow); + // CutRow() returns the row removed equal to ioRow, if there was one + + morkRow* GetOid(morkEnv* ev, const mdbOid* inOid); + // GetOid() returns the row equal to inRid, or else nil + + morkRow* GetRow(morkEnv* ev, const morkRow* ioRow); + // GetRow() returns the row equal to ioRow, or else nil + + // note the rows are owned elsewhere, usuall by morkRowSpace + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowProbeMap(morkRowProbeMap* me, + morkEnv* ev, morkRowProbeMap** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongRowProbeMap(morkRowProbeMap* me, + morkEnv* ev, morkRowProbeMap** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +class morkRowProbeMapIter: public morkProbeMapIter{ // typesafe wrapper class + +public: + morkRowProbeMapIter(morkEnv* ev, morkRowProbeMap* ioMap) + : morkProbeMapIter(ev, ioMap) { } + + morkRowProbeMapIter( ) : morkProbeMapIter() { } + void InitRowMapIter(morkEnv* ev, morkRowProbeMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr) + { return this->First(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr) + { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr) + { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); } + + mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr) + { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWMAP_ */ diff --git a/db/mork/src/morkRowObject.cpp b/db/mork/src/morkRowObject.cpp new file mode 100644 index 0000000000..884b5be4c2 --- /dev/null +++ b/db/mork/src/morkRowObject.cpp @@ -0,0 +1,623 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKROWCELLCURSOR_ +#include "morkRowCellCursor.h" +#endif + +#ifndef _MORKCELLOBJECT_ +#include "morkCellObject.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkRowObject::CloseMorkNode(morkEnv* ev) // CloseRowObject() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseRowObject(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowObject::~morkRowObject() // assert CloseRowObject() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowObject::morkRowObject(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkRow* ioRow, morkStore* ioStore) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mRowObject_Row( 0 ) +, mRowObject_Store( 0 ) +{ + if ( ev->Good() ) + { + if ( ioRow && ioStore ) + { + mRowObject_Row = ioRow; + mRowObject_Store = ioStore; // morkRowObjects don't ref-cnt the owning store. + + if ( ev->Good() ) + mNode_Derived = morkDerived_kRowObject; + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkRowObject, morkObject, nsIMdbRow) +// { ===== begin nsIMdbCollection methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkRowObject::GetSeed(nsIMdbEnv* mev, + mdb_seed* outSeed) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + *outSeed = (mdb_seed) mRowObject_Row->mRow_Seed; + outErr = ev->AsErr(); + } + return outErr; +} +NS_IMETHODIMP +morkRowObject::GetCount(nsIMdbEnv* mev, + mdb_count* outCount) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + *outCount = (mdb_count) mRowObject_Row->mRow_Length; + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::GetPort(nsIMdbEnv* mev, + nsIMdbPort** acqPort) +{ + nsresult outErr = NS_OK; + nsIMdbPort* outPort = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowSpace* rowSpace = mRowObject_Row->mRow_Space; + if ( rowSpace && rowSpace->mSpace_Store ) + { + morkStore* store = mRowObject_Row->GetRowSpaceStore(ev); + if ( store ) + outPort = store->AcquireStoreHandle(ev); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqPort ) + *acqPort = outPort; + + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkRowObject::GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* mev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) +{ + return this->GetRowCellCursor(mev, inMemberPos, + (nsIMdbRowCellCursor**) acqCursor); +} +// } ----- end cursor methods ----- + +// { ----- begin ID methods ----- +NS_IMETHODIMP +morkRowObject::GetOid(nsIMdbEnv* mev, + mdbOid* outOid) +{ + *outOid = mRowObject_Row->mRow_Oid; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + return (ev) ? ev->AsErr() : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +morkRowObject::BecomeContent(nsIMdbEnv* mev, + const mdbOid* inOid) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember row->MaybeDirtySpaceStoreAndRow(); +} +// } ----- end ID methods ----- + +// { ----- begin activity dropping methods ----- +NS_IMETHODIMP +morkRowObject::DropActivity( // tell collection usage no longer expected + nsIMdbEnv* mev) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end activity dropping methods ----- + +// } ===== end nsIMdbCollection methods ===== + +// { ===== begin nsIMdbRow methods ===== + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkRowObject::GetRowCellCursor( // make a cursor starting iteration at inCellPos + nsIMdbEnv* mev, // context + mdb_pos inPos, // zero-based ordinal position of cell in row + nsIMdbRowCellCursor** acqCursor) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + nsIMdbRowCellCursor* outCursor = 0; + if ( ev ) + { + morkRowCellCursor* cursor = mRowObject_Row->NewRowCellCursor(ev, inPos); + if ( cursor ) + { + if ( ev->Good() ) + { + cursor->mCursor_Seed = (mork_seed) inPos; + outCursor = cursor; + NS_ADDREF(cursor); + } + } + outErr = ev->AsErr(); + } + if ( acqCursor ) + *acqCursor = outCursor; + return outErr; +} +// } ----- end cursor methods ----- + +// { ----- begin column methods ----- +NS_IMETHODIMP +morkRowObject::AddColumn( // make sure a particular column is inside row + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) +{ + nsresult outErr = NS_ERROR_FAILURE; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store && mRowObject_Row) + mRowObject_Row->AddColumn(ev, inColumn, inYarn, mRowObject_Store); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::CutColumn( // make sure a column is absent from the row + nsIMdbEnv* mev, // context + mdb_column inColumn) +{ + nsresult outErr = NS_ERROR_FAILURE; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mRowObject_Row->CutColumn(ev, inColumn); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::CutAllColumns( // remove all columns from the row + nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mRowObject_Row->CutAllColumns(ev); + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end column methods ----- + +// { ----- begin cell methods ----- +NS_IMETHODIMP +morkRowObject::NewCell( // get cell for specified column, or add new one + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + GetCell(mev, inColumn, acqCell); + if ( !*acqCell ) + { + if ( mRowObject_Store ) + { + mdbYarn yarn; // to pass empty yarn into morkRowObject::AddColumn() + yarn.mYarn_Buf = 0; + yarn.mYarn_Fill = 0; + yarn.mYarn_Size = 0; + yarn.mYarn_More = 0; + yarn.mYarn_Form = 0; + yarn.mYarn_Grow = 0; + AddColumn(ev, inColumn, &yarn); + GetCell(mev, inColumn, acqCell); + } + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::AddCell( // copy a cell from another row to this row + nsIMdbEnv* mev, // context + const nsIMdbCell* inCell) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkCell* cell = 0; + morkCellObject* cellObj = (morkCellObject*) inCell; + if ( cellObj->CanUseCell(mev, morkBool_kFalse, &outErr, &cell) ) + { + + morkRow* cellRow = cellObj->mCellObject_Row; + if ( cellRow ) + { + if ( mRowObject_Row != cellRow ) + { + morkStore* store = mRowObject_Row->GetRowSpaceStore(ev); + morkStore* cellStore = cellRow->GetRowSpaceStore(ev); + if ( store && cellStore ) + { + mork_column col = cell->GetColumn(); + morkAtom* atom = cell->mCell_Atom; + mdbYarn yarn; + morkAtom::AliasYarn(atom, &yarn); // works even when atom is nil + + if ( store != cellStore ) + col = store->CopyToken(ev, col, cellStore); + if ( ev->Good() ) + AddColumn(ev, col, &yarn); + } + else + ev->NilPointerError(); + } + } + else + ev->NilPointerError(); + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::GetCell( // find a cell in this row + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) +{ + nsresult outErr = NS_OK; + nsIMdbCell* outCell = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + + if ( ev ) + { + if ( inColumn ) + { + mork_pos pos = 0; + morkCell* cell = mRowObject_Row->GetCell(ev, inColumn, &pos); + if ( cell ) + { + outCell = mRowObject_Row->AcquireCellHandle(ev, cell, inColumn, pos); + } + } + else + mRowObject_Row->ZeroColumnError(ev); + + outErr = ev->AsErr(); + } + if ( acqCell ) + *acqCell = outCell; + return outErr; +} + +NS_IMETHODIMP +morkRowObject::EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + EmptyAllCells(ev); + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end cell methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkRowObject::AddRow( // add all cells in another row to this one + nsIMdbEnv* mev, // context + nsIMdbRow* ioSourceRow) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRow* unsafeSource = (morkRow*) ioSourceRow; // unsafe cast +// if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) ) + { + mRowObject_Row->AddRow(ev, unsafeSource); + } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::SetRow( // make exact duplicate of another row + nsIMdbEnv* mev, // context + nsIMdbRow* ioSourceRow) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject *sourceObject = (morkRowObject *) ioSourceRow; // unsafe cast + morkRow* unsafeSource = sourceObject->mRowObject_Row; +// if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) ) + { + mRowObject_Row->SetRow(ev, unsafeSource); + } + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end row methods ----- + +// { ----- begin blob methods ----- +NS_IMETHODIMP +morkRowObject::SetCellYarn( // synonym for AddColumn() + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store ) + AddColumn(ev, inColumn, inYarn); + + outErr = ev->AsErr(); + } + return outErr; +} +NS_IMETHODIMP +morkRowObject::GetCellYarn( + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to read + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store && mRowObject_Row) + { + morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn); + atom->GetYarn(outYarn); + // note nil atom works and sets yarn correctly + } + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::AliasCellYarn( + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to alias + mdbYarn* outYarn) // writes ALL yarn slots +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store && mRowObject_Row) + { + morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn); + morkAtom::AliasYarn(atom, outYarn); + // note nil atom works and sets yarn correctly + } + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::NextCellYarn(nsIMdbEnv* mev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +// +// The ioColumn argument is an inout parameter which initially contains the +// last column accessed and returns the next column corresponding to the +// content read into the yarn. Callers should start with a zero column +// value to say 'no previous column', which causes the first column to be +// read. Then the value returned in ioColumn is perfect for the next call +// to NextCellYarn(), since it will then be the previous column accessed. +// Callers need only examine the column token returned to see which cell +// in the row is being read into the yarn. When no more columns remain, +// and the iteration has ended, ioColumn will return a zero token again. +// So iterating over cells starts and ends with a zero column token. +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store && mRowObject_Row) + mRowObject_Row->NextColumn(ev, ioColumn, outYarn); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkRowObject::SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* mev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) // writes some yarn slots +// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form +// Callers can pass nil for outYarn to indicate no interest in content, so +// only the outColumn value is returned. NOTE to subclasses: you must be +// able to ignore outYarn when the pointer is nil; please do not crash. + +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mRowObject_Store && mRowObject_Row) + mRowObject_Row->SeekColumn(ev, inPos, outColumn, outYarn); + + outErr = ev->AsErr(); + } + return outErr; +} + +// } ----- end blob methods ----- + + +// } ===== end nsIMdbRow methods ===== + + + +/*public non-poly*/ void +morkRowObject::CloseRowObject(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkRow* row = mRowObject_Row; + mRowObject_Row = 0; + this->CloseObject(ev); + this->MarkShut(); + + if ( row ) + { + MORK_ASSERT(row->mRow_Object == this); + if ( row->mRow_Object == this ) + { + row->mRow_Object = 0; // just nil this slot -- cut ref down below + + mRowObject_Store = 0; // morkRowObjects don't ref-cnt the owning store. + + this->CutWeakRef(ev->AsMdbEnv()); // do last, because it might self destroy + } + } + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkRowObject::NonRowObjectTypeError(morkEnv* ev) +{ + ev->NewError("non morkRowObject"); +} + +/*static*/ void +morkRowObject::NilRowError(morkEnv* ev) +{ + ev->NewError("nil mRowObject_Row"); +} + +/*static*/ void +morkRowObject::NilStoreError(morkEnv* ev) +{ + ev->NewError("nil mRowObject_Store"); +} + +/*static*/ void +morkRowObject::RowObjectRowNotSelfError(morkEnv* ev) +{ + ev->NewError("mRowObject_Row->mRow_Object != self"); +} + + +nsIMdbRow* +morkRowObject::AcquireRowHandle(morkEnv* ev) // mObject_Handle +{ + AddRef(); + return this; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkRowObject.h b/db/mork/src/morkRowObject.h new file mode 100644 index 0000000000..c30494a455 --- /dev/null +++ b/db/mork/src/morkRowObject.h @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKROWOBJECT_ +#define _MORKROWOBJECT_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbRow; +#define morkDerived_kRowObject /*i*/ 0x724F /* ascii 'rO' */ + +class morkRowObject : public morkObject, public nsIMdbRow { // + +public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + + morkRow* mRowObject_Row; // non-refcounted alias to morkRow + morkStore* mRowObject_Store; // non-refcounted ptr to store containing row + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowObject() only if open + +public: // morkRowObject construction & destruction + morkRowObject(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkRow* ioRow, morkStore* ioStore); + void CloseRowObject(morkEnv* ev); // called by CloseMorkNode(); + +// { ===== begin nsIMdbCollection methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) override; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) override; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) override; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) override; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) override; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) override; + // } ----- end activity dropping methods ----- + +// } ===== end nsIMdbCollection methods ===== +// { ===== begin nsIMdbRow methods ===== + + // { ----- begin cursor methods ----- + NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRowCellCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin column methods ----- + NS_IMETHOD AddColumn( // make sure a particular column is inside row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + const mdbYarn* inYarn) override; // cell value to install + + NS_IMETHOD CutColumn( // make sure a column is absent from the row + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // column to ensure absent from row + + NS_IMETHOD CutAllColumns( // remove all columns from the row + nsIMdbEnv* ev) override; // context + // } ----- end column methods ----- + + // { ----- begin cell methods ----- + NS_IMETHOD NewCell( // get cell for specified column, or add new one + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to add + nsIMdbCell** acqCell) override; // cell column and value + + NS_IMETHOD AddCell( // copy a cell from another row to this row + nsIMdbEnv* ev, // context + const nsIMdbCell* inCell) override; // cell column and value + + NS_IMETHOD GetCell( // find a cell in this row + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to find + nsIMdbCell** acqCell) override; // cell for specified column, or null + + NS_IMETHOD EmptyAllCells( // make all cells in row empty of content + nsIMdbEnv* ev) override; // context + // } ----- end cell methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD AddRow( // add all cells in another row to this one + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) override; // row to union with + + NS_IMETHOD SetRow( // make exact duplicate of another row + nsIMdbEnv* ev, // context + nsIMdbRow* ioSourceRow) override; // row to duplicate + // } ----- end row methods ----- + + // { ----- begin blob methods ----- + NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn() + mdb_column inColumn, // column to write + const mdbYarn* inYarn) override; // reads from yarn slots + // make this text object contain content from the yarn's buffer + + NS_IMETHOD GetCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to read + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + + NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev, + mdb_column inColumn, // column to alias + mdbYarn* outYarn) override; // writes ALL yarn slots + + NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn() + mdb_column* ioColumn, // next column to read + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // + // The ioColumn argument is an inout parameter which initially contains the + // last column accessed and returns the next column corresponding to the + // content read into the yarn. Callers should start with a zero column + // value to say 'no previous column', which causes the first column to be + // read. Then the value returned in ioColumn is perfect for the next call + // to NextCellYarn(), since it will then be the previous column accessed. + // Callers need only examine the column token returned to see which cell + // in the row is being read into the yarn. When no more columns remain, + // and the iteration has ended, ioColumn will return a zero token again. + // So iterating over cells starts and ends with a zero column token. + + NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell() + nsIMdbEnv* ev, // context + mdb_pos inPos, // position of cell in row sequence + mdb_column* outColumn, // column for this particular cell + mdbYarn* outYarn) override; // writes some yarn slots + // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form + // Callers can pass nil for outYarn to indicate no interest in content, so + // only the outColumn value is returned. NOTE to subclasses: you must be + // able to ignore outYarn when the pointer is nil; please do not crash. + + // } ----- end blob methods ----- + +// } ===== end nsIMdbRow methods ===== + +private: // copying is not allowed + morkRowObject(const morkRowObject& other); + morkRowObject& operator=(const morkRowObject& other); + virtual ~morkRowObject(); // assert that CloseRowObject() executed earlier + +public: // dynamic type identification + mork_bool IsRowObject() const + { return IsNode() && mNode_Derived == morkDerived_kRowObject; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonRowObjectTypeError(morkEnv* ev); + static void NilRowError(morkEnv* ev); + static void NilStoreError(morkEnv* ev); + static void RowObjectRowNotSelfError(morkEnv* ev); + +public: // other row node methods + + nsIMdbRow* AcquireRowHandle(morkEnv* ev); // mObject_Handle + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowObject(morkRowObject* me, + morkEnv* ev, morkRowObject** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongRowObject(morkRowObject* me, + morkEnv* ev, morkRowObject** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWOBJECT_ */ diff --git a/db/mork/src/morkRowSpace.cpp b/db/mork/src/morkRowSpace.cpp new file mode 100644 index 0000000000..10f2416f5a --- /dev/null +++ b/db/mork/src/morkRowSpace.cpp @@ -0,0 +1,632 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKATOMMAP_ +#include "morkAtomMap.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkRowSpace::CloseMorkNode(morkEnv* ev) // CloseRowSpace() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseRowSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkRowSpace::~morkRowSpace() // assert CloseRowSpace() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkRowSpace::morkRowSpace(morkEnv* ev, + const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap) +, mRowSpace_SlotHeap( ioSlotHeap ) +, mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap, + morkRowSpace_kStartRowMapSlotCount) +, mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap) +, mRowSpace_NextTableId( 1 ) +, mRowSpace_NextRowId( 1 ) + +, mRowSpace_IndexCount( 0 ) +{ + morkAtomRowMap** cache = mRowSpace_IndexCache; + morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize; + while ( cache < cacheEnd ) + *cache++ = 0; // put nil into every slot of cache table + + if ( ev->Good() ) + { + if ( ioSlotHeap ) + { + mNode_Derived = morkDerived_kRowSpace; + + // the morkSpace base constructor handles any dirty propagation + } + else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void +morkRowSpace::CloseRowSpace(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkAtomRowMap** cache = mRowSpace_IndexCache; + morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize; + --cache; // prepare for preincrement: + while ( ++cache < cacheEnd ) + { + if ( *cache ) + morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache); + } + + mRowSpace_Tables.CloseMorkNode(ev); + + morkStore* store = mSpace_Store; + if ( store ) + this->CutAllRows(ev, &store->mStore_Pool); + + mRowSpace_Rows.CloseMorkNode(ev); + this->CloseSpace(ev); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkRowSpace::NonRowSpaceTypeError(morkEnv* ev) +{ + ev->NewError("non morkRowSpace"); +} + +/*static*/ void +morkRowSpace::ZeroKindError(morkEnv* ev) +{ + ev->NewError("zero table kind"); +} + +/*static*/ void +morkRowSpace::ZeroScopeError(morkEnv* ev) +{ + ev->NewError("zero row scope"); +} + +/*static*/ void +morkRowSpace::ZeroTidError(morkEnv* ev) +{ + ev->NewError("zero table ID"); +} + +/*static*/ void +morkRowSpace::MinusOneRidError(morkEnv* ev) +{ + ev->NewError("row ID is -1"); +} + +///*static*/ void +//morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev) +//{ +// ev->NewError("zero row ID"); +//} + +///*static*/ void +//morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev) +//{ +//} + +mork_num +morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool) +{ + if ( this->IsRowSpaceClean() ) + this->MaybeDirtyStoreAndSpace(); + + mork_num outSlots = mRowSpace_Rows.MapFill(); + +#ifdef MORK_ENABLE_ZONE_ARENAS + MORK_USED_2(ev, ioPool); + return 0; +#else /*MORK_ENABLE_ZONE_ARENAS*/ + morkZone* zone = &mSpace_Store->mStore_Zone; + morkRow* r = 0; // old key row in the map + mork_change* c = 0; + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter i(ev, &mRowSpace_Rows); +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter i(ev, &mRowSpace_Rows); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + + for ( c = i.FirstRow(ev, &r); c && ev->Good(); + c = i.NextRow(ev, &r) ) + { + if ( r ) + { + if ( r->IsRow() ) + { + if ( r->mRow_Object ) + { + morkRowObject::SlotWeakRowObject((morkRowObject*) 0, ev, + &r->mRow_Object); + } + ioPool->ZapRow(ev, r, zone); + } + else + r->NonRowTypeWarning(ev); + } + else + ev->NilPointerError(); + +#ifdef MORK_ENABLE_PROBE_MAPS + // cut nothing from the map +#else /*MORK_ENABLE_PROBE_MAPS*/ + i.CutHereRow(ev, /*key*/ (morkRow**) 0); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + } +#endif /*MORK_ENABLE_ZONE_ARENAS*/ + + + return outSlots; +} + +morkTable* +morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind) +{ + if ( inTableKind ) + { +#ifdef MORK_BEAD_OVER_NODE_MAPS + + morkTableMapIter i(ev, &mRowSpace_Tables); + morkTable* table = i.FirstTable(ev); + for ( ; table && ev->Good(); table = i.NextTable(ev) ) + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key = 0; // nil pointer to suppress key access + morkTable* table = 0; // old table in the map + + mork_change* c = 0; + morkTableMapIter i(ev, &mRowSpace_Tables); + for ( c = i.FirstTable(ev, key, &table); c && ev->Good(); + c = i.NextTable(ev, key, &table) ) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if ( table->mTable_Kind == inTableKind ) + return table; + } + } + else + this->ZeroKindError(ev); + + return (morkTable*) 0; +} + +morkTable* +morkRowSpace::NewTableWithTid(morkEnv* ev, mork_tid inTid, + mork_kind inTableKind, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + morkStore* store = mSpace_Store; + + if ( inTableKind && store ) + { + mdb_bool mustBeUnique = morkBool_kFalse; + nsIMdbHeap* heap = store->mPort_Heap; + morkTable* table = new(*heap, ev) + morkTable(ev, morkUsage::kHeap, heap, store, heap, this, + inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique); + if ( table ) + { + if ( mRowSpace_Tables.AddTable(ev, table) ) + { + outTable = table; + if ( mRowSpace_NextTableId <= inTid ) + mRowSpace_NextTableId = inTid + 1; + } + + if ( this->IsRowSpaceClean() && store->mStore_CanDirty ) + this->MaybeDirtyStoreAndSpace(); // morkTable does already + + } + } + else if ( store ) + this->ZeroKindError(ev); + else + this->NilSpaceStoreError(ev); + + return outTable; +} + +morkTable* +morkRowSpace::NewTable(morkEnv* ev, mork_kind inTableKind, + mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + morkStore* store = mSpace_Store; + + if ( inTableKind && store ) + { + if ( inMustBeUnique ) // need to look for existing table first? + outTable = this->FindTableByKind(ev, inTableKind); + + if ( !outTable && ev->Good() ) + { + mork_tid id = this->MakeNewTableId(ev); + if ( id ) + { + nsIMdbHeap* heap = mSpace_Store->mPort_Heap; + morkTable* table = new(*heap, ev) + morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this, + inOptionalMetaRowOid, id, inTableKind, inMustBeUnique); + if ( table ) + { + if ( mRowSpace_Tables.AddTable(ev, table) ) + outTable = table; + else + table->Release(); + + if ( this->IsRowSpaceClean() && store->mStore_CanDirty ) + this->MaybeDirtyStoreAndSpace(); // morkTable does already + } + } + } + } + else if ( store ) + this->ZeroKindError(ev); + else + this->NilSpaceStoreError(ev); + + return outTable; +} + +mork_tid +morkRowSpace::MakeNewTableId(morkEnv* ev) +{ + mork_tid outTid = 0; + mork_tid id = mRowSpace_NextTableId; + mork_num count = 9; // try up to eight times + + while ( !outTid && --count ) // still trying to find an unused table ID? + { + if ( !mRowSpace_Tables.GetTable(ev, id) ) + outTid = id; + else + { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + mRowSpace_NextTableId = id + 1; + return outTid; +} + +#define MAX_AUTO_ID (morkRow_kMinusOneRid - 2) +mork_rid +morkRowSpace::MakeNewRowId(morkEnv* ev) +{ + mork_rid outRid = 0; + mork_rid id = mRowSpace_NextRowId; + mork_num count = 9; // try up to eight times + mdbOid oid; + oid.mOid_Scope = this->SpaceScope(); + + // still trying to find an unused row ID? + while (!outRid && --count && id <= MAX_AUTO_ID) + { + oid.mOid_Id = id; + if ( !mRowSpace_Rows.GetOid(ev, &oid) ) + outRid = id; + else + { + MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems + ++id; + } + } + + if (id < MAX_AUTO_ID) + mRowSpace_NextRowId = id + 1; + return outRid; +} + +morkAtomRowMap* +morkRowSpace::make_index(morkEnv* ev, mork_column inCol) +{ + morkAtomRowMap* outMap = 0; + nsIMdbHeap* heap = mRowSpace_SlotHeap; + if ( heap ) // have expected heap for allocations? + { + morkAtomRowMap* map = new(*heap, ev) + morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol); + + if ( map ) // able to create new map index? + { + if ( ev->Good() ) // no errors during construction? + { +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter i(ev, &mRowSpace_Rows); +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter i(ev, &mRowSpace_Rows); +#endif /*MORK_ENABLE_PROBE_MAPS*/ + mork_change* c = 0; + morkRow* row = 0; + mork_aid aidKey = 0; + + for ( c = i.FirstRow(ev, &row); c && ev->Good(); + c = i.NextRow(ev, &row) ) // another row in space? + { + aidKey = row->GetCellAtomAid(ev, inCol); + if ( aidKey ) // row has indexed attribute? + map->AddAid(ev, aidKey, row); // include in map + } + } + if ( ev->Good() ) // no errors constructing index? + outMap = map; // return from function + else + map->CutStrongRef(ev); // discard map on error + } + } + else + ev->NilPointerError(); + + return outMap; +} + +morkAtomRowMap* +morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol) +{ + morkAtomRowMap* outMap = this->FindMap(ev, inCol); + + if ( !outMap && ev->Good() ) // no such existing index? + { + if ( mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount ) + { + morkAtomRowMap* map = this->make_index(ev, inCol); + if ( map ) // created a new index for col? + { + mork_count wrap = 0; // count times wrap-around occurs + morkAtomRowMap** slot = mRowSpace_IndexCache; // table + morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize; + slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash + while ( *slot ) // empty slot not yet found? + { + if ( ++slot >= end ) // wrap around? + { + slot = mRowSpace_IndexCache; // back to table start + if ( ++wrap > 1 ) // wrapped more than once? + { + ev->NewError("no free cache slots"); // disaster + break; // end while loop + } + } + } + if ( ev->Good() ) // everything went just fine? + { + ++mRowSpace_IndexCount; // note another new map + *slot = map; // install map in the hash table + outMap = map; // return the new map from function + } + else + map->CutStrongRef(ev); // discard map on error + } + } + else + ev->NewError("too many indexes"); // why so many indexes? + } + return outMap; +} + +morkAtomRowMap* +morkRowSpace::FindMap(morkEnv* ev, mork_column inCol) +{ + if ( mRowSpace_IndexCount && ev->Good() ) + { + mork_count wrap = 0; // count times wrap-around occurs + morkAtomRowMap** slot = mRowSpace_IndexCache; // table + morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize; + slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash + morkAtomRowMap* map = *slot; + while ( map ) // another used slot to examine? + { + if ( inCol == map->mAtomRowMap_IndexColumn ) // found col? + return map; + if ( ++slot >= end ) // wrap around? + { + slot = mRowSpace_IndexCache; + if ( ++wrap > 1 ) // wrapped more than once? + return (morkAtomRowMap*) 0; // stop searching + } + map = *slot; + } + } + return (morkAtomRowMap*) 0; +} + +morkRow* +morkRowSpace::FindRow(morkEnv* ev, mork_column inCol, const mdbYarn* inYarn) +{ + morkRow* outRow = 0; + + // if yarn hasn't been atomized, there can't be a corresponding row, + // so pass in false to not create the row - should help history bloat + morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, false); + if ( atom ) // have or created an atom corresponding to input yarn? + { + mork_aid atomAid = atom->GetBookAtomAid(); + if ( atomAid ) // atom has an identity for use in hash table? + { + morkAtomRowMap* map = this->ForceMap(ev, inCol); + if ( map ) // able to find or create index for col? + { + outRow = map->GetAid(ev, atomAid); // search for row + } + } + } + + return outRow; +} + +morkRow* +morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid) +{ + morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid); + MORK_ASSERT(outRow==0); + if ( !outRow && ev->Good() ) + { + morkStore* store = mSpace_Store; + if ( store ) + { + morkPool* pool = this->GetSpaceStorePool(); + morkRow* row = pool->NewRow(ev, &store->mStore_Zone); + if ( row ) + { + row->InitRow(ev, inOid, this, /*length*/ 0, pool); + + if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) ) + { + outRow = row; + mork_rid rid = inOid->mOid_Id; + if ( mRowSpace_NextRowId <= rid ) + mRowSpace_NextRowId = rid + 1; + } + else + pool->ZapRow(ev, row, &store->mStore_Zone); + + if ( this->IsRowSpaceClean() && store->mStore_CanDirty ) + this->MaybeDirtyStoreAndSpace(); // InitRow() does already + } + } + else + this->NilSpaceStoreError(ev); + } + return outRow; +} + +morkRow* +morkRowSpace::NewRow(morkEnv* ev) +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + mork_rid id = this->MakeNewRowId(ev); + if ( id ) + { + morkStore* store = mSpace_Store; + if ( store ) + { + mdbOid oid; + oid.mOid_Scope = this->SpaceScope(); + oid.mOid_Id = id; + morkPool* pool = this->GetSpaceStorePool(); + morkRow* row = pool->NewRow(ev, &store->mStore_Zone); + if ( row ) + { + row->InitRow(ev, &oid, this, /*length*/ 0, pool); + + if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) ) + outRow = row; + else + pool->ZapRow(ev, row, &store->mStore_Zone); + + if ( this->IsRowSpaceClean() && store->mStore_CanDirty ) + this->MaybeDirtyStoreAndSpace(); // InitRow() does already + } + } + else + this->NilSpaceStoreError(ev); + } + } + return outRow; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +morkRowSpaceMap::~morkRowSpaceMap() +{ +} + +morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) + : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kRowSpaceMap; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkRowSpace.h b/db/mork/src/morkRowSpace.h new file mode 100644 index 0000000000..0ef331bb80 --- /dev/null +++ b/db/mork/src/morkRowSpace.h @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKROWSPACE_ +#define _MORKROWSPACE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKARRAY_ +#include "morkArray.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowSpace /*i*/ 0x7253 /* ascii 'rS' */ + +#define morkRowSpace_kStartRowMapSlotCount 11 + +#define morkRowSpace_kMaxIndexCount 8 /* no more indexes than this */ +#define morkRowSpace_kPrimeCacheSize 17 /* should be prime number */ + +class morkAtomRowMap; + +/*| morkRowSpace: +|*/ +class morkRowSpace : public morkSpace { // + +// public: // slots inherited from morkSpace (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkStore* mSpace_Store; // weak ref to containing store + + // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment + +public: // state is public because the entire Mork system is private + + nsIMdbHeap* mRowSpace_SlotHeap; + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMap mRowSpace_Rows; // hash table of morkRow instances +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMap mRowSpace_Rows; // hash table of morkRow instances +#endif /*MORK_ENABLE_PROBE_MAPS*/ + morkTableMap mRowSpace_Tables; // all the tables in this row scope + + mork_tid mRowSpace_NextTableId; // for auto-assigning table IDs + mork_rid mRowSpace_NextRowId; // for auto-assigning row IDs + + mork_count mRowSpace_IndexCount; // if nonzero, row indexes exist + + // every nonzero slot in IndexCache is a strong ref to a morkAtomRowMap: + morkAtomRowMap* mRowSpace_IndexCache[ morkRowSpace_kPrimeCacheSize ]; + + morkDeque mRowSpace_TablesByPriority[ morkPriority_kCount ]; + +public: // more specific dirty methods for row space: + void SetRowSpaceDirty() { this->SetNodeDirty(); } + void SetRowSpaceClean() { this->SetNodeClean(); } + + mork_bool IsRowSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsRowSpaceDirty() const { return this->IsNodeDirty(); } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowSpace() only if open + virtual ~morkRowSpace(); // assert that CloseRowSpace() executed earlier + +public: // morkMap construction & destruction + morkRowSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap); + void CloseRowSpace(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsRowSpace() const + { return IsNode() && mNode_Derived == morkDerived_kRowSpace; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonRowSpaceTypeError(morkEnv* ev); + static void ZeroScopeError(morkEnv* ev); + static void ZeroKindError(morkEnv* ev); + static void ZeroTidError(morkEnv* ev); + static void MinusOneRidError(morkEnv* ev); + + //static void ExpectAutoIdOnlyError(morkEnv* ev); + //static void ExpectAutoIdNeverError(morkEnv* ev); + +public: // other space methods + + mork_num CutAllRows(morkEnv* ev, morkPool* ioPool); + // CutAllRows() puts all rows and cells back into the pool. + + morkTable* NewTable(morkEnv* ev, mork_kind inTableKind, + mdb_bool inMustBeUnique, const mdbOid* inOptionalMetaRowOid); + + morkTable* NewTableWithTid(morkEnv* ev, mork_tid inTid, + mork_kind inTableKind, const mdbOid* inOptionalMetaRowOid); + + morkTable* FindTableByKind(morkEnv* ev, mork_kind inTableKind); + morkTable* FindTableByTid(morkEnv* ev, mork_tid inTid) + { return mRowSpace_Tables.GetTable(ev, inTid); } + + mork_tid MakeNewTableId(morkEnv* ev); + mork_rid MakeNewRowId(morkEnv* ev); + + // morkRow* FindRowByRid(morkEnv* ev, mork_rid inRid) + // { return (morkRow*) mRowSpace_Rows.GetRow(ev, inRid); } + + morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid); + morkRow* NewRow(morkEnv* ev); + + morkRow* FindRow(morkEnv* ev, mork_column inColumn, const mdbYarn* inYarn); + + morkAtomRowMap* ForceMap(morkEnv* ev, mork_column inColumn); + morkAtomRowMap* FindMap(morkEnv* ev, mork_column inColumn); + +protected: // internal utilities + morkAtomRowMap* make_index(morkEnv* ev, mork_column inColumn); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakRowSpace(morkRowSpace* me, + morkEnv* ev, morkRowSpace** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongRowSpace(morkRowSpace* me, + morkEnv* ev, morkRowSpace** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kRowSpaceMap /*i*/ 0x725A /* ascii 'rZ' */ + +/*| morkRowSpaceMap: maps mork_scope -> morkRowSpace +|*/ +class morkRowSpaceMap : public morkNodeMap { // for mapping tokens to tables + +public: + + virtual ~morkRowSpaceMap(); + morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + +public: // other map methods + + mork_bool AddRowSpace(morkEnv* ev, morkRowSpace* ioRowSpace) + { return this->AddNode(ev, ioRowSpace->SpaceScope(), ioRowSpace); } + // the AddRowSpace() boolean return equals ev->Good(). + + mork_bool CutRowSpace(morkEnv* ev, mork_scope inScope) + { return this->CutNode(ev, inScope); } + // The CutRowSpace() boolean return indicates whether removal happened. + + morkRowSpace* GetRowSpace(morkEnv* ev, mork_scope inScope) + { return (morkRowSpace*) this->GetNode(ev, inScope); } + // Note the returned space does NOT have an increase in refcount for this. + + mork_num CutAllRowSpaces(morkEnv* ev) + { return this->CutAllNodes(ev); } + // CutAllRowSpaces() releases all the referenced table values. +}; + +class morkRowSpaceMapIter: public morkMapIter{ // typesafe wrapper class + +public: + morkRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkRowSpaceMapIter( ) : morkMapIter() { } + void InitRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* + FirstRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace) + { return this->First(ev, outScope, outRowSpace); } + + mork_change* + NextRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace) + { return this->Next(ev, outScope, outRowSpace); } + + mork_change* + HereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace) + { return this->Here(ev, outScope, outRowSpace); } + + mork_change* + CutHereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace) + { return this->CutHere(ev, outScope, outRowSpace); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKROWSPACE_ */ diff --git a/db/mork/src/morkSearchRowCursor.cpp b/db/mork/src/morkSearchRowCursor.cpp new file mode 100644 index 0000000000..5bc0b43721 --- /dev/null +++ b/db/mork/src/morkSearchRowCursor.cpp @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKSEARCHROWCURSOR_ +#include "morkSearchRowCursor.h" +#endif + +#ifndef _MORKUNIQROWCURSOR_ +#include "morkUniqRowCursor.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkSearchRowCursor::CloseMorkNode(morkEnv* ev) // CloseSearchRowCursor() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseSearchRowCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkSearchRowCursor::~morkSearchRowCursor() // CloseSearchRowCursor() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkSearchRowCursor::morkSearchRowCursor(morkEnv* ev, + const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos) +: morkTableRowCursor(ev, inUsage, ioHeap, ioTable, inRowPos) +// , mSortingRowCursor_Sorting( 0 ) +{ + if ( ev->Good() ) + { + if ( ioTable ) + { + // morkSorting::SlotWeakSorting(ioSorting, ev, &mSortingRowCursor_Sorting); + if ( ev->Good() ) + { + // mNode_Derived = morkDerived_kTableRowCursor; + // mNode_Derived must stay equal to kTableRowCursor + } + } + else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void +morkSearchRowCursor::CloseSearchRowCursor(morkEnv* ev) +{ + if ( this->IsNode() ) + { + // morkSorting::SlotWeakSorting((morkSorting*) 0, ev, &mSortingRowCursor_Sorting); + this->CloseTableRowCursor(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkSearchRowCursor::NonSearchRowCursorTypeError(morkEnv* ev) +{ + ev->NewError("non morkSearchRowCursor"); +} + +morkUniqRowCursor* +morkSearchRowCursor::MakeUniqCursor(morkEnv* ev) +{ + morkUniqRowCursor* outCursor = 0; + + return outCursor; +} + +#if 0 +orkinTableRowCursor* +morkSearchRowCursor::AcquireUniqueRowCursorHandle(morkEnv* ev) +{ + orkinTableRowCursor* outCursor = 0; + + morkUniqRowCursor* uniqCursor = this->MakeUniqCursor(ev); + if ( uniqCursor ) + { + outCursor = uniqCursor->AcquireTableRowCursorHandle(ev); + uniqCursor->CutStrongRef(ev); + } + return outCursor; +} +#endif +mork_bool +morkSearchRowCursor::CanHaveDupRowMembers(morkEnv* ev) +{ + return morkBool_kTrue; // true is correct +} + +mork_count +morkSearchRowCursor::GetMemberCount(morkEnv* ev) +{ + morkTable* table = mTableRowCursor_Table; + if ( table ) + return table->mTable_RowArray.mArray_Fill; + else + return 0; +} + +morkRow* +morkSearchRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos) +{ + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if ( table ) + { + } + else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkSearchRowCursor.h b/db/mork/src/morkSearchRowCursor.h new file mode 100644 index 0000000000..019610643c --- /dev/null +++ b/db/mork/src/morkSearchRowCursor.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKSEARCHROWCURSOR_ +#define _MORKSEARCHROWCURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKTABLEROWCURSOR_ +#include "morkTableRowCursor.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class morkUniqRowCursor; +class orkinTableRowCursor; +// #define morkDerived_kSearchRowCursor /*i*/ 0x7352 /* ascii 'sR' */ + +class morkSearchRowCursor : public morkTableRowCursor { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + // morkTable* mTableRowCursor_Table; // weak ref to table + +public: // state is public because the entire Mork system is private + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev); // CloseSearchRowCursor() + virtual ~morkSearchRowCursor(); // assert that close executed earlier + +public: // morkSearchRowCursor construction & destruction + morkSearchRowCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos); + void CloseSearchRowCursor(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkSearchRowCursor(const morkSearchRowCursor& other); + morkSearchRowCursor& operator=(const morkSearchRowCursor& other); + +public: // dynamic type identification + // mork_bool IsSearchRowCursor() const + // { return IsNode() && mNode_Derived == morkDerived_kSearchRowCursor; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonSearchRowCursorTypeError(morkEnv* ev); + +public: // uniquify + + morkUniqRowCursor* MakeUniqCursor(morkEnv* ev); + +public: // other search row cursor methods + + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + +#if 0 + virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev); +#endif + + // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakSearchRowCursor(morkSearchRowCursor* me, + morkEnv* ev, morkSearchRowCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongSearchRowCursor(morkSearchRowCursor* me, + morkEnv* ev, morkSearchRowCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSEARCHROWCURSOR_ */ diff --git a/db/mork/src/morkSink.cpp b/db/mork/src/morkSink.cpp new file mode 100644 index 0000000000..1d4089c0ab --- /dev/null +++ b/db/mork/src/morkSink.cpp @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKSINK_ +#include "morkSink.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*virtual*/ morkSink::~morkSink() +{ + mSink_At = 0; + mSink_End = 0; +} + +/*virtual*/ void +morkSpool::FlushSink(morkEnv* ev) // sync mSpool_Coil->mBuf_Fill +{ + morkCoil* coil = mSpool_Coil; + if ( coil ) + { + mork_u1* body = (mork_u1*) coil->mBuf_Body; + if ( body ) + { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if ( at >= body && at <= end ) // expected cursor order? + { + mork_fill fill = (mork_fill) (at - body); // current content size + if ( fill <= coil->mBlob_Size ) + coil->mBuf_Fill = fill; + else + { + coil->BlobFillOverSizeError(ev); + coil->mBuf_Fill = coil->mBlob_Size; // make it safe + } + } + else + this->BadSpoolCursorOrderError(ev); + } + else + coil->NilBufBodyError(ev); + } + else + this->NilSpoolCoilError(ev); +} + +/*virtual*/ void +morkSpool::SpillPutc(morkEnv* ev, int c) // grow coil and write byte +{ + morkCoil* coil = mSpool_Coil; + if ( coil ) + { + mork_u1* body = (mork_u1*) coil->mBuf_Body; + if ( body ) + { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if ( at >= body && at <= end ) // expected cursor order? + { + mork_size size = coil->mBlob_Size; + mork_fill fill = (mork_fill) (at - body); // current content size + if ( fill <= size ) // less content than medium size? + { + coil->mBuf_Fill = fill; + if ( at >= end ) // need to grow the coil? + { + if ( size > 2048 ) // grow slower over 2K? + size += 512; + else + { + mork_size growth = ( size * 4 ) / 3; // grow by 33% + if ( growth < 64 ) // grow faster under (64 * 3)? + growth = 64; + size += growth; + } + if ( coil->GrowCoil(ev, size) ) // made coil bigger? + { + body = (mork_u1*) coil->mBuf_Body; + if ( body ) // have a coil body? + { + mSink_At = at = body + fill; + mSink_End = end = body + coil->mBlob_Size; + } + else + coil->NilBufBodyError(ev); + } + } + if ( ev->Good() ) // seem ready to write byte c? + { + if ( at < end ) // morkSink::Putc() would succeed? + { + *at++ = (mork_u1) c; + mSink_At = at; + coil->mBuf_Fill = fill + 1; + } + else + this->BadSpoolCursorOrderError(ev); + } + } + else // fill exceeds size + { + coil->BlobFillOverSizeError(ev); + coil->mBuf_Fill = coil->mBlob_Size; // make it safe + } + } + else + this->BadSpoolCursorOrderError(ev); + } + else + coil->NilBufBodyError(ev); + } + else + this->NilSpoolCoilError(ev); +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// public: // public non-poly morkSink methods + +/*virtual*/ +morkSpool::~morkSpool() +// Zero all slots to show this sink is disabled, but destroy no memory. +// Note it is typically unnecessary to flush this coil sink, since all +// content is written directly to the coil without any buffering. +{ + mSink_At = 0; + mSink_End = 0; + mSpool_Coil = 0; +} + +morkSpool::morkSpool(morkEnv* ev, morkCoil* ioCoil) +// After installing the coil, calls Seek(ev, 0) to prepare for writing. +: morkSink() +, mSpool_Coil( 0 ) +{ + mSink_At = 0; // set correctly later in Seek() + mSink_End = 0; // set correctly later in Seek() + + if ( ev->Good() ) + { + if ( ioCoil ) + { + mSpool_Coil = ioCoil; + this->Seek(ev, /*pos*/ 0); + } + else + ev->NilPointerError(); + } +} + +// ----- All boolean return values below are equal to ev->Good(): ----- + +/*static*/ void +morkSpool::BadSpoolCursorOrderError(morkEnv* ev) +{ + ev->NewError("bad morkSpool cursor order"); +} + +/*static*/ void +morkSpool::NilSpoolCoilError(morkEnv* ev) +{ + ev->NewError("nil mSpool_Coil"); +} + +mork_bool +morkSpool::Seek(morkEnv* ev, mork_pos inPos) +// Changed the current write position in coil's buffer to inPos. +// For example, to start writing the coil from scratch, use inPos==0. +{ + morkCoil* coil = mSpool_Coil; + if ( coil ) + { + mork_size minSize = (mork_size) (inPos + 64); + + if ( coil->mBlob_Size < minSize ) + coil->GrowCoil(ev, minSize); + + if ( ev->Good() ) + { + coil->mBuf_Fill = (mork_fill) inPos; + mork_u1* body = (mork_u1*) coil->mBuf_Body; + if ( body ) + { + mSink_At = body + inPos; + mSink_End = body + coil->mBlob_Size; + } + else + coil->NilBufBodyError(ev); + } + } + else + this->NilSpoolCoilError(ev); + + return ev->Good(); +} + +mork_bool +morkSpool::Write(morkEnv* ev, const void* inBuf, mork_size inSize) +// write inSize bytes of inBuf to current position inside coil's buffer +{ + // This method is conceptually very similar to morkStream::Write(), + // and this code was written while looking at that method for clues. + + morkCoil* coil = mSpool_Coil; + if ( coil ) + { + mork_u1* body = (mork_u1*) coil->mBuf_Body; + if ( body ) + { + if ( inBuf && inSize ) // anything to write? + { + mork_u1* at = mSink_At; + mork_u1* end = mSink_End; + if ( at >= body && at <= end ) // expected cursor order? + { + // note coil->mBuf_Fill can be stale after morkSink::Putc(): + mork_pos fill = at - body; // current content size + mork_num space = (mork_num) (end - at); // space left in body + if ( space < inSize ) // not enough to hold write? + { + mork_size minGrowth = space + 16; + mork_size minSize = coil->mBlob_Size + minGrowth; + if ( coil->GrowCoil(ev, minSize) ) + { + body = (mork_u1*) coil->mBuf_Body; + if ( body ) + { + mSink_At = at = body + fill; + mSink_End = end = body + coil->mBlob_Size; + space = (mork_num) (end - at); // space left in body + } + else + coil->NilBufBodyError(ev); + } + } + if ( ev->Good() ) + { + if ( space >= inSize ) // enough room to hold write? + { + MORK_MEMCPY(at, inBuf, inSize); // into body + mSink_At = at + inSize; // advance past written bytes + coil->mBuf_Fill = fill + inSize; // "flush" to fix fill + } + else + ev->NewError("insufficient morkSpool space"); + } + } + else + this->BadSpoolCursorOrderError(ev); + } + } + else + coil->NilBufBodyError(ev); + } + else + this->NilSpoolCoilError(ev); + + return ev->Good(); +} + +mork_bool +morkSpool::PutString(morkEnv* ev, const char* inString) +// call Write() with inBuf=inString and inSize=strlen(inString), +// unless inString is null, in which case we then do nothing at all. +{ + if ( inString ) + { + mork_size size = MORK_STRLEN(inString); + this->Write(ev, inString, size); + } + return ev->Good(); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkSink.h b/db/mork/src/morkSink.h new file mode 100644 index 0000000000..4d501e76c8 --- /dev/null +++ b/db/mork/src/morkSink.h @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKSINK_ +#define _MORKSINK_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| morkSink is intended to be a very cheap buffered i/o sink which +**| writes to bufs and other strings a single byte at a time. The +**| basic idea is that writing a single byte has a very cheap average +**| cost, because a polymophic function call need only occur when the +**| space between At and End is exhausted. The rest of the time a +**| very cheap inline method will write a byte, and then bump a pointer. +**| +**|| At: the current position in some sequence of bytes at which to +**| write the next byte put into the sink. Presumably At points into +**| the private storage of some space which is not yet filled (except +**| when At reaches End, and the overflow must then spill). Note both +**| At and End are zeroed in the destructor to help show that a sink +**| is no longer usable; this is safe because At==End causes the case +**| where SpillPutc() is called to handled an exhausted buffer space. +**| +**|| End: an address one byte past the last byte which can be written +**| without needing to make a buffer larger than previously. When At +**| and End are equal, this means there is no space to write a byte, +**| and that some underlying buffer space must be grown before another +**| byte can be written. Note At must always be less than or equal to +**| End, and otherwise an important invariant has failed severely. +**| +**|| Buf: this original class slot has been commented out in the new +**| and more abstract version of this sink class, but the general idea +**| behind this slot should be explained to help design subclasses. +**| Each subclass should provide space into which At and End can point, +**| where End is beyond the last writable byte, and At is less than or +**| equal to this point inside the same buffer. With some kinds of +**| medium, such as writing to an instance of morkBlob, it is feasible +**| to point directly into the final resting place for all the content +**| written to the medium. Other mediums such as files, which write +**| only through function calls, will typically need a local buffer +**| to efficiently accumulate many bytes between such function calls. +**| +**|| FlushSink: this flush method should move any buffered content to +**| its final destination. For example, for buffered writes to a +**| string medium, where string methods are function calls and not just +**| inline macros, it is faster to accumulate many bytes in a small +**| local buffer and then move these en masse later in a single call. +**| +**|| SpillPutc: when At is greater than or equal to End, this means an +**| underlying buffer has become full, so the buffer must be flushed +**| before a new byte can be written. The intention is that SpillPutc() +**| will be equivalent to calling FlushSink() followed by another call +**| to Putc(), where the flush is expected to make At less then End once +**| again. Except that FlushSink() need not make the underlying buffer +**| any larger, and SpillPutc() typically must make room for more bytes. +**| Note subclasses might want to guard against the case that both At +**| and End are null, which happens when a sink is destroyed, which sets +**| both these pointers to null as an indication the sink is disabled. +|*/ +class morkSink { + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public sink virtual methods + + virtual void FlushSink(morkEnv* ev) = 0; + virtual void SpillPutc(morkEnv* ev, int c) = 0; + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // member variables + + mork_u1* mSink_At; // pointer into mSink_Buf + mork_u1* mSink_End; // one byte past last content byte + +// define morkSink_kBufSize 256 /* small enough to go on stack */ + + // mork_u1 mSink_Buf[ morkSink_kBufSize + 4 ]; + // want plus one for any needed end null byte; use plus 4 for alignment + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkSink methods + + virtual ~morkSink(); // zero both At and End; virtual for subclasses + morkSink() { } // does nothing; subclasses must set At and End suitably + + void Putc(morkEnv* ev, int c) + { + if ( mSink_At < mSink_End ) + *mSink_At++ = (mork_u1) c; + else + this->SpillPutc(ev, c); + } +}; + +/*| morkSpool: an output sink that efficiently writes individual bytes +**| or entire byte sequences to a coil instance, which grows as needed by +**| using the heap instance in the coil to grow the internal buffer. +**| +**|| Note we do not "own" the coil referenced by mSpool_Coil, and +**| the lifetime of the coil is expected to equal or exceed that of this +**| sink by some external means. Typical usage might involve keeping an +**| instance of morkCoil and an instance of morkSpool in the same +**| owning parent object, which uses the spool with the associated coil. +|*/ +class morkSpool : public morkSink { // for buffered i/o to a morkCoil + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public sink virtual methods + + // when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong: + + virtual void FlushSink(morkEnv* ev); // sync mSpool_Coil->mBuf_Fill + virtual void SpillPutc(morkEnv* ev, int c); // grow coil and write byte + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // member variables + morkCoil* mSpool_Coil; // destination medium for written bytes + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkSink methods + + static void BadSpoolCursorOrderError(morkEnv* ev); + static void NilSpoolCoilError(morkEnv* ev); + + virtual ~morkSpool(); + // Zero all slots to show this sink is disabled, but destroy no memory. + // Note it is typically unnecessary to flush this coil sink, since all + // content is written directly to the coil without any buffering. + + morkSpool(morkEnv* ev, morkCoil* ioCoil); + // After installing the coil, calls Seek(ev, 0) to prepare for writing. + + // ----- All boolean return values below are equal to ev->Good(): ----- + + mork_bool Seek(morkEnv* ev, mork_pos inPos); + // Changed the current write position in coil's buffer to inPos. + // For example, to start writing the coil from scratch, use inPos==0. + + mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize); + // write inSize bytes of inBuf to current position inside coil's buffer + + mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer) + { return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill); } + + mork_bool PutString(morkEnv* ev, const char* inString); + // call Write() with inBuf=inString and inSize=strlen(inString), + // unless inString is null, in which case we then do nothing at all. +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSINK_ */ diff --git a/db/mork/src/morkSpace.cpp b/db/mork/src/morkSpace.cpp new file mode 100644 index 0000000000..b19a6e737e --- /dev/null +++ b/db/mork/src/morkSpace.cpp @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKSPACE_ +#include "morkSpace.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkSpace::CloseMorkNode(morkEnv* ev) // CloseSpace() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseSpace(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkSpace::~morkSpace() // assert CloseSpace() executed earlier +{ + MORK_ASSERT(SpaceScope()==0); + MORK_ASSERT(mSpace_Store==0); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +//morkSpace::morkSpace(morkEnv* ev, const morkUsage& inUsage, +// nsIMdbHeap* ioNodeHeap, const morkMapForm& inForm, +// nsIMdbHeap* ioSlotHeap) +//: morkNode(ev, inUsage, ioNodeHeap) +//, mSpace_Map(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap) +//{ +// ev->StubMethodOnlyError(); +//} + +/*public non-poly*/ +morkSpace::morkSpace(morkEnv* ev, + const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +: morkBead(ev, inUsage, ioHeap, inScope) +, mSpace_Store( 0 ) +, mSpace_DoAutoIDs( morkBool_kFalse ) +, mSpace_HaveDoneAutoIDs( morkBool_kFalse ) +, mSpace_CanDirty( morkBool_kFalse ) // only when store can be dirtied +{ + if ( ev->Good() ) + { + if ( ioStore && ioSlotHeap ) + { + morkStore::SlotWeakStore(ioStore, ev, &mSpace_Store); + + mSpace_CanDirty = ioStore->mStore_CanDirty; + if ( mSpace_CanDirty ) // this new space dirties the store? + this->MaybeDirtyStoreAndSpace(); + + if ( ev->Good() ) + mNode_Derived = morkDerived_kSpace; + } + else + ev->NilPointerError(); + } +} + +/*public non-poly*/ void +morkSpace::CloseSpace(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkStore::SlotWeakStore((morkStore*) 0, ev, &mSpace_Store); + mBead_Color = 0; // this->CloseBead(); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkSpace::NonAsciiSpaceScopeName(morkEnv* ev) +{ + ev->NewError("SpaceScope() > 0x7F"); +} + +/*static*/ void +morkSpace::NilSpaceStoreError(morkEnv* ev) +{ + ev->NewError("nil mSpace_Store"); +} + +morkPool* morkSpace::GetSpaceStorePool() const +{ + return &mSpace_Store->mStore_Pool; +} + +mork_bool morkSpace::MaybeDirtyStoreAndSpace() +{ + morkStore* store = mSpace_Store; + if ( store && store->mStore_CanDirty ) + { + store->SetStoreDirty(); + mSpace_CanDirty = morkBool_kTrue; + } + + if ( mSpace_CanDirty ) + { + this->SetSpaceDirty(); + return morkBool_kTrue; + } + + return morkBool_kFalse; +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkSpace.h b/db/mork/src/morkSpace.h new file mode 100644 index 0000000000..a0a12d116b --- /dev/null +++ b/db/mork/src/morkSpace.h @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKSPACE_ +#define _MORKSPACE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKBEAD_ +#include "morkBead.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkSpace_kInitialSpaceSlots /*i*/ 1024 /* default */ +#define morkDerived_kSpace /*i*/ 0x5370 /* ascii 'Sp' */ + +/*| morkSpace: +|*/ +class morkSpace : public morkBead { // + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + +public: // bead color setter & getter replace obsolete member mTable_Id: + + mork_tid SpaceScope() const { return mBead_Color; } + void SetSpaceScope(mork_scope inScope) { mBead_Color = inScope; } + +public: // state is public because the entire Mork system is private + + morkStore* mSpace_Store; // weak ref to containing store + + mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs + mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs + mork_bool mSpace_CanDirty; // changes imply the store becomes dirty? + mork_u1 mSpace_Pad; // pad to u4 alignment + +public: // more specific dirty methods for space: + void SetSpaceDirty() { this->SetNodeDirty(); } + void SetSpaceClean() { this->SetNodeClean(); } + + mork_bool IsSpaceClean() const { return this->IsNodeClean(); } + mork_bool IsSpaceDirty() const { return this->IsNodeDirty(); } + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev); // CloseSpace() only if open + virtual ~morkSpace(); // assert that CloseSpace() executed earlier + +public: // morkMap construction & destruction + //morkSpace(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + // const morkMapForm& inForm, nsIMdbHeap* ioSlotHeap); + + morkSpace(morkEnv* ev, const morkUsage& inUsage,mork_scope inScope, + morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap); + void CloseSpace(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsSpace() const + { return IsNode() && mNode_Derived == morkDerived_kSpace; } +// } ===== end morkNode methods ===== + +public: // other space methods + + mork_bool MaybeDirtyStoreAndSpace(); + + static void NonAsciiSpaceScopeName(morkEnv* ev); + static void NilSpaceStoreError(morkEnv* ev); + + morkPool* GetSpaceStorePool() const; + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakSpace(morkSpace* me, + morkEnv* ev, morkSpace** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongSpace(morkSpace* me, + morkEnv* ev, morkSpace** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSPACE_ */ diff --git a/db/mork/src/morkStore.cpp b/db/mork/src/morkStore.cpp new file mode 100644 index 0000000000..47f145c83b --- /dev/null +++ b/db/mork/src/morkStore.cpp @@ -0,0 +1,2290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKFACTORY_ +#include "morkFactory.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKTHUMB_ +#include "morkThumb.h" +#endif +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKBUILDER_ +#include "morkBuilder.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKSTREAM_ +#include "morkStream.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKPORTTABLECURSOR_ +#include "morkPortTableCursor.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKPARSER_ +#include "morkParser.h" +#endif + +#include "nsCOMPtr.h" + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkStore::CloseMorkNode(morkEnv* ev) // ClosePort() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseStore(ev); + this->MarkShut(); + } +} + +/*public non-poly*/ void +morkStore::ClosePort(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap); + this->CloseObject(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +/*public virtual*/ +morkStore::~morkStore() // assert CloseStore() executed earlier +{ + MOZ_COUNT_DTOR(morkStore); + if (IsOpenNode()) + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mStore_File==0); + MORK_ASSERT(mStore_InStream==0); + MORK_ASSERT(mStore_OutStream==0); + MORK_ASSERT(mStore_Builder==0); + MORK_ASSERT(mStore_OidAtomSpace==0); + MORK_ASSERT(mStore_GroundAtomSpace==0); + MORK_ASSERT(mStore_GroundColumnSpace==0); + MORK_ASSERT(mStore_RowSpaces.IsShutNode()); + MORK_ASSERT(mStore_AtomSpaces.IsShutNode()); + MORK_ASSERT(mStore_Pool.IsShutNode()); +} + +/*public non-poly*/ +morkStore::morkStore(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance + morkFactory* inFactory, // the factory for this + nsIMdbHeap* ioPortHeap // the heap to hold all content in the port + ) +: morkObject(ev, inUsage, ioNodeHeap, morkColor_kNone, (morkHandle*) 0) +, mPort_Env( ev ) +, mPort_Factory( 0 ) +, mPort_Heap( 0 ) +, mStore_OidAtomSpace( 0 ) +, mStore_GroundAtomSpace( 0 ) +, mStore_GroundColumnSpace( 0 ) + +, mStore_File( 0 ) +, mStore_InStream( 0 ) +, mStore_Builder( 0 ) +, mStore_OutStream( 0 ) + +, mStore_RowSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap) +, mStore_AtomSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap) +, mStore_Zone(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap) +, mStore_Pool(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap) + +, mStore_CommitGroupIdentity( 0 ) + +, mStore_FirstCommitGroupPos( 0 ) +, mStore_SecondCommitGroupPos( 0 ) + +// disable auto-assignment of atom IDs until someone knows it is okay: +, mStore_CanAutoAssignAtomIdentity( morkBool_kFalse ) +, mStore_CanDirty( morkBool_kFalse ) // not until the store is open +, mStore_CanWriteIncremental( morkBool_kTrue ) // always with few exceptions +{ + MOZ_COUNT_CTOR(morkStore); + if ( ev->Good() ) + { + if ( inFactory && ioPortHeap ) + { + morkFactory::SlotWeakFactory(inFactory, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap(ioPortHeap, ev, &mPort_Heap); + if ( ev->Good() ) + mNode_Derived = morkDerived_kPort; + } + else + ev->NilPointerError(); + } + if ( ev->Good() ) + { + mNode_Derived = morkDerived_kStore; + + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkStore, morkObject, nsIMdbStore) + +/*public non-poly*/ void +morkStore::CloseStore(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + + nsIMdbFile* file = mStore_File; + file->AddRef(); + + morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mStore_OidAtomSpace); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mStore_GroundAtomSpace); + morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev, + &mStore_GroundColumnSpace); + mStore_RowSpaces.CloseMorkNode(ev); + mStore_AtomSpaces.CloseMorkNode(ev); + morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder); + + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, + &mStore_File); + + file->Release(); + + morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream); + morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_OutStream); + + mStore_Pool.CloseMorkNode(ev); + mStore_Zone.CloseMorkNode(ev); + this->ClosePort(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + + +mork_bool morkStore::DoPreferLargeOverCompressCommit(morkEnv* ev) + // true when mStore_CanWriteIncremental && store has file large enough +{ + nsIMdbFile* file = mStore_File; + if ( file && mStore_CanWriteIncremental ) + { + mdb_pos fileEof = 0; + file->Eof(ev->AsMdbEnv(), &fileEof); + if ( ev->Good() && fileEof > 128 ) + return morkBool_kTrue; + } + return morkBool_kFalse; +} + +mork_percent morkStore::PercentOfStoreWasted(morkEnv* ev) +{ + mork_percent outPercent = 0; + nsIMdbFile* file = mStore_File; + + if ( file ) + { + mork_pos firstPos = mStore_FirstCommitGroupPos; + mork_pos secondPos = mStore_SecondCommitGroupPos; + if ( firstPos || secondPos ) + { + if ( firstPos < 512 && secondPos > firstPos ) + firstPos = secondPos; // better approximation of first commit + + mork_pos fileLength = 0; + file->Eof(ev->AsMdbEnv(), &fileLength); // end of file + if ( ev->Good() && fileLength > firstPos ) + { + mork_size groupContent = fileLength - firstPos; + outPercent = ( groupContent * 100 ) / fileLength; + } + } + } + else + this->NilStoreFileError(ev); + + return outPercent; +} + +void +morkStore::SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty) +{ + mStore_CanDirty = inCanDirty; + + mork_change* c = 0; + mork_scope* key = 0; // ignore keys in maps + + if ( ev->Good() ) + { + morkAtomSpaceMapIter asi(ev, &mStore_AtomSpaces); + + morkAtomSpace* atomSpace = 0; // old val node in the map + + for ( c = asi.FirstAtomSpace(ev, key, &atomSpace); c && ev->Good(); + c = asi.NextAtomSpace(ev, key, &atomSpace) ) + { + if ( atomSpace ) + { + if ( atomSpace->IsAtomSpace() ) + atomSpace->mSpace_CanDirty = inCanDirty; + else + atomSpace->NonAtomSpaceTypeError(ev); + } + else + ev->NilPointerError(); + } + } + + if ( ev->Good() ) + { + morkRowSpaceMapIter rsi(ev, &mStore_RowSpaces); + morkRowSpace* rowSpace = 0; // old val node in the map + + for ( c = rsi.FirstRowSpace(ev, key, &rowSpace); c && ev->Good(); + c = rsi.NextRowSpace(ev, key, &rowSpace) ) + { + if ( rowSpace ) + { + if ( rowSpace->IsRowSpace() ) + rowSpace->mSpace_CanDirty = inCanDirty; + else + rowSpace->NonRowSpaceTypeError(ev); + } + } + } +} + +void +morkStore::RenumberAllCollectableContent(morkEnv* ev) +{ + MORK_USED_1(ev); + // do nothing currently +} + +nsIMdbStore* +morkStore::AcquireStoreHandle(morkEnv* ev) +{ + return this; +} + + +morkFarBookAtom* +morkStore::StageAliasAsFarBookAtom(morkEnv* ev, const morkMid* inMid, + morkAtomSpace* ioSpace, mork_cscode inForm) +{ + if ( inMid && inMid->mMid_Buf ) + { + const morkBuf* buf = inMid->mMid_Buf; + mork_size length = buf->mBuf_Fill; + if ( length <= morkBookAtom_kMaxBodySize ) + { + mork_aid dummyAid = 1; + //mStore_BookAtom.InitMaxBookAtom(ev, *buf, + // inForm, ioSpace, dummyAid); + + mStore_FarBookAtom.InitFarBookAtom(ev, *buf, + inForm, ioSpace, dummyAid); + return &mStore_FarBookAtom; + } + } + else + ev->NilPointerError(); + + return (morkFarBookAtom*) 0; +} + +morkFarBookAtom* +morkStore::StageYarnAsFarBookAtom(morkEnv* ev, const mdbYarn* inYarn, + morkAtomSpace* ioSpace) +{ + if ( inYarn && inYarn->mYarn_Buf ) + { + mork_size length = inYarn->mYarn_Fill; + if ( length <= morkBookAtom_kMaxBodySize ) + { + morkBuf buf(inYarn->mYarn_Buf, length); + mork_aid dummyAid = 1; + //mStore_BookAtom.InitMaxBookAtom(ev, buf, + // inYarn->mYarn_Form, ioSpace, dummyAid); + mStore_FarBookAtom.InitFarBookAtom(ev, buf, + inYarn->mYarn_Form, ioSpace, dummyAid); + return &mStore_FarBookAtom; + } + } + else + ev->NilPointerError(); + + return (morkFarBookAtom*) 0; +} + +morkFarBookAtom* +morkStore::StageStringAsFarBookAtom(morkEnv* ev, const char* inString, + mork_cscode inForm, morkAtomSpace* ioSpace) +{ + if ( inString ) + { + mork_size length = MORK_STRLEN(inString); + if ( length <= morkBookAtom_kMaxBodySize ) + { + morkBuf buf(inString, length); + mork_aid dummyAid = 1; + //mStore_BookAtom.InitMaxBookAtom(ev, buf, inForm, ioSpace, dummyAid); + mStore_FarBookAtom.InitFarBookAtom(ev, buf, inForm, ioSpace, dummyAid); + return &mStore_FarBookAtom; + } + } + else + ev->NilPointerError(); + + return (morkFarBookAtom*) 0; +} + +morkAtomSpace* morkStore::LazyGetOidAtomSpace(morkEnv* ev) +{ + MORK_USED_1(ev); + if ( !mStore_OidAtomSpace ) + { + } + return mStore_OidAtomSpace; +} + +morkAtomSpace* morkStore::LazyGetGroundAtomSpace(morkEnv* ev) +{ + if ( !mStore_GroundAtomSpace ) + { + mork_scope atomScope = morkStore_kValueSpaceScope; + nsIMdbHeap* heap = mPort_Heap; + morkAtomSpace* space = new(*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap); + + if ( space ) // successful space creation? + { + this->MaybeDirtyStore(); + + mStore_GroundAtomSpace = space; // transfer strong ref to this slot + mStore_AtomSpaces.AddAtomSpace(ev, space); + } + } + return mStore_GroundAtomSpace; +} + +morkAtomSpace* morkStore::LazyGetGroundColumnSpace(morkEnv* ev) +{ + if ( !mStore_GroundColumnSpace ) + { + mork_scope atomScope = morkStore_kGroundColumnSpace; + nsIMdbHeap* heap = mPort_Heap; + morkAtomSpace* space = new(*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap); + + if ( space ) // successful space creation? + { + this->MaybeDirtyStore(); + + mStore_GroundColumnSpace = space; // transfer strong ref to this slot + mStore_AtomSpaces.AddAtomSpace(ev, space); + } + } + return mStore_GroundColumnSpace; +} + +morkStream* morkStore::LazyGetInStream(morkEnv* ev) +{ + if ( !mStore_InStream ) + { + nsIMdbFile* file = mStore_File; + if ( file ) + { + morkStream* stream = new(*mPort_Heap, ev) + morkStream(ev, morkUsage::kHeap, mPort_Heap, file, + morkStore_kStreamBufSize, /*frozen*/ morkBool_kTrue); + if ( stream ) + { + this->MaybeDirtyStore(); + mStore_InStream = stream; // transfer strong ref to this slot + } + } + else + this->NilStoreFileError(ev); + } + return mStore_InStream; +} + +morkStream* morkStore::LazyGetOutStream(morkEnv* ev) +{ + if ( !mStore_OutStream ) + { + nsIMdbFile* file = mStore_File; + if ( file ) + { + morkStream* stream = new(*mPort_Heap, ev) + morkStream(ev, morkUsage::kHeap, mPort_Heap, file, + morkStore_kStreamBufSize, /*frozen*/ morkBool_kFalse); + if ( stream ) + { + this->MaybeDirtyStore(); + mStore_InStream = stream; // transfer strong ref to this slot + } + } + else + this->NilStoreFileError(ev); + } + return mStore_OutStream; +} + +void +morkStore::ForgetBuilder(morkEnv* ev) +{ + if ( mStore_Builder ) + morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder); + if ( mStore_InStream ) + morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream); +} + +morkBuilder* morkStore::LazyGetBuilder(morkEnv* ev) +{ + if ( !mStore_Builder ) + { + morkStream* stream = this->LazyGetInStream(ev); + if ( stream ) + { + nsIMdbHeap* heap = mPort_Heap; + morkBuilder* builder = new(*heap, ev) + morkBuilder(ev, morkUsage::kHeap, heap, stream, + morkBuilder_kDefaultBytesPerParseSegment, heap, this); + if ( builder ) + { + mStore_Builder = builder; // transfer strong ref to this slot + } + } + } + return mStore_Builder; +} + +morkRowSpace* +morkStore::LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope) +{ + morkRowSpace* outSpace = mStore_RowSpaces.GetRowSpace(ev, inRowScope); + if ( !outSpace && ev->Good() ) // try to make new space? + { + nsIMdbHeap* heap = mPort_Heap; + outSpace = new(*heap, ev) + morkRowSpace(ev, morkUsage::kHeap, inRowScope, this, heap, heap); + + if ( outSpace ) // successful space creation? + { + this->MaybeDirtyStore(); + + // note adding to node map creates its own strong ref... + if ( mStore_RowSpaces.AddRowSpace(ev, outSpace) ) + outSpace->CutStrongRef(ev); // ...so we can drop our strong ref + } + } + return outSpace; +} + +morkAtomSpace* +morkStore::LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope) +{ + morkAtomSpace* outSpace = mStore_AtomSpaces.GetAtomSpace(ev, inAtomScope); + if ( !outSpace && ev->Good() ) // try to make new space? + { + if ( inAtomScope == morkStore_kValueSpaceScope ) + outSpace = this->LazyGetGroundAtomSpace(ev); + + else if ( inAtomScope == morkStore_kGroundColumnSpace ) + outSpace = this->LazyGetGroundColumnSpace(ev); + else + { + nsIMdbHeap* heap = mPort_Heap; + outSpace = new(*heap, ev) + morkAtomSpace(ev, morkUsage::kHeap, inAtomScope, this, heap, heap); + + if ( outSpace ) // successful space creation? + { + this->MaybeDirtyStore(); + + // note adding to node map creates its own strong ref... + if ( mStore_AtomSpaces.AddAtomSpace(ev, outSpace) ) + outSpace->CutStrongRef(ev); // ...so we can drop our strong ref + } + } + } + return outSpace; +} + +/*static*/ void +morkStore::NonStoreTypeError(morkEnv* ev) +{ + ev->NewError("non morkStore"); +} + +/*static*/ void +morkStore::NilStoreFileError(morkEnv* ev) +{ + ev->NewError("nil mStore_File"); +} + +/*static*/ void +morkStore::CannotAutoAssignAtomIdentityError(morkEnv* ev) +{ + ev->NewError("false mStore_CanAutoAssignAtomIdentity"); +} + + +mork_bool +morkStore::OpenStoreFile(morkEnv* ev, mork_bool inFrozen, + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy) +{ + MORK_USED_2(inOpenPolicy,inFrozen); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File); + + // if ( ev->Good() ) + // { + // morkFile* file = morkFile::OpenOldFile(ev, mPort_Heap, + // inFilePath, inFrozen); + // if ( ioFile ) + // { + // if ( ev->Good() ) + // morkFile::SlotStrongFile(file, ev, &mStore_File); + // else + // file->CutStrongRef(ev); + // + // } + // } + return ev->Good(); +} + +mork_bool +morkStore::CreateStoreFile(morkEnv* ev, + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy) +{ + MORK_USED_1(inOpenPolicy); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File); + + return ev->Good(); +} + +morkAtom* +morkStore::CopyAtom(morkEnv* ev, const morkAtom* inAtom) +// copy inAtom (from some other store) over to this store +{ + morkAtom* outAtom = 0; + if ( inAtom ) + { + mdbYarn yarn; + if ( morkAtom::AliasYarn(inAtom, &yarn) ) + outAtom = this->YarnToAtom(ev, &yarn, true /* create */); + } + return outAtom; +} + +morkAtom* +morkStore::YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, bool createIfMissing /* = true */) +{ + morkAtom* outAtom = 0; + if ( ev->Good() ) + { + morkAtomSpace* groundSpace = this->LazyGetGroundAtomSpace(ev); + if ( groundSpace ) + { + morkFarBookAtom* keyAtom = + this->StageYarnAsFarBookAtom(ev, inYarn, groundSpace); + + if ( keyAtom ) + { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + outAtom = map->GetAtom(ev, keyAtom); + if ( !outAtom && createIfMissing) + { + this->MaybeDirtyStore(); + outAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + } + } + else if ( ev->Good() ) + { + morkBuf b(inYarn->mYarn_Buf, inYarn->mYarn_Fill); + morkZone* z = &mStore_Zone; + outAtom = mStore_Pool.NewAnonAtom(ev, b, inYarn->mYarn_Form, z); + } + } + } + return outAtom; +} + +mork_bool +morkStore::MidToOid(morkEnv* ev, const morkMid& inMid, mdbOid* outOid) +{ + *outOid = inMid.mMid_Oid; + const morkBuf* buf = inMid.mMid_Buf; + if ( buf && !outOid->mOid_Scope ) + { + if ( buf->mBuf_Fill <= morkBookAtom_kMaxBodySize ) + { + if ( buf->mBuf_Fill == 1 ) + { + mork_u1* name = (mork_u1*) buf->mBuf_Body; + if ( name ) + { + outOid->mOid_Scope = (mork_scope) *name; + return ev->Good(); + } + } + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if ( groundSpace ) + { + mork_cscode form = 0; // default + mork_aid aid = 1; // dummy + mStore_FarBookAtom.InitFarBookAtom(ev, *buf, form, groundSpace, aid); + morkFarBookAtom* keyAtom = &mStore_FarBookAtom; + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if ( bookAtom ) + outOid->mOid_Scope = bookAtom->mBookAtom_Id; + else + { + this->MaybeDirtyStore(); + bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + if ( bookAtom ) + { + outOid->mOid_Scope = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } + return ev->Good(); +} + +morkRow* +morkStore::MidToRow(morkEnv* ev, const morkMid& inMid) +{ + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToRow(ev, &tempOid); +} + +morkTable* +morkStore::MidToTable(morkEnv* ev, const morkMid& inMid) +{ + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToTable(ev, &tempOid, /*metarow*/ (mdbOid*) 0); +} + +mork_bool +morkStore::MidToYarn(morkEnv* ev, const morkMid& inMid, mdbYarn* outYarn) +{ + mdbOid tempOid; + this->MidToOid(ev, inMid, &tempOid); + return this->OidToYarn(ev, tempOid, outYarn); +} + +mork_bool +morkStore::OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn) +{ + morkBookAtom* atom = 0; + + morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, inOid.mOid_Scope); + if ( atomSpace ) + { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + atom = map->GetAid(ev, (mork_aid) inOid.mOid_Id); + } + atom->GetYarn(outYarn); // note this is safe even when atom==nil + + return ev->Good(); +} + +morkBookAtom* +morkStore::MidToAtom(morkEnv* ev, const morkMid& inMid) +{ + morkBookAtom* outAtom = 0; + mdbOid oid; + if ( this->MidToOid(ev, inMid, &oid) ) + { + morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, oid.mOid_Scope); + if ( atomSpace ) + { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + outAtom = map->GetAid(ev, (mork_aid) oid.mOid_Id); + } + } + return outAtom; +} + +/*static*/ void +morkStore::SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken, + mdbYarn* outYarn) +{ + MORK_USED_1(ev); + if ( outYarn->mYarn_Buf && outYarn->mYarn_Size ) // any space in yarn at all? + { + mork_u1* buf = (mork_u1*) outYarn->mYarn_Buf; // for byte arithmetic + buf[ 0 ] = (mork_u1) inToken; // write the single byte + outYarn->mYarn_Fill = 1; + outYarn->mYarn_More = 0; + } + else // just record we could not write the single byte + { + outYarn->mYarn_More = 1; + outYarn->mYarn_Fill = 0; + } +} + +void +morkStore::TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName) +{ + if ( inToken > morkAtomSpace_kMaxSevenBitAid ) + { + morkBookAtom* atom = 0; + morkAtomSpace* space = mStore_GroundColumnSpace; + if ( space ) + atom = space->mAtomSpace_AtomAids.GetAid(ev, (mork_aid) inToken); + + atom->GetYarn(outTokenName); // note this is safe even when atom==nil + } + else // token is an "immediate" single byte string representation? + this->SmallTokenToOneByteYarn(ev, inToken, outTokenName); +} + +// void +// morkStore::SyncTokenIdChange(morkEnv* ev, const morkBookAtom* inAtom, +// const mdbOid* inOid) +// { +// mork_token mStore_MorkNoneToken; // token for "mork:none" // fill=9 +// mork_column mStore_CharsetToken; // token for "charset" // fill=7 +// mork_column mStore_AtomScopeToken; // token for "atomScope" // fill=9 +// mork_column mStore_RowScopeToken; // token for "rowScope" // fill=8 +// mork_column mStore_TableScopeToken; // token for "tableScope" // fill=10 +// mork_column mStore_ColumnScopeToken; // token for "columnScope" // fill=11 +// mork_kind mStore_TableKindToken; // token for "tableKind" // fill=9 +// ---------------------ruler-for-token-length-above---123456789012 +// +// if ( inOid->mOid_Scope == morkStore_kColumnSpaceScope && inAtom->IsWeeBook() ) +// { +// const mork_u1* body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body; +// mork_size size = inAtom->mAtom_Size; +// +// if ( size >= 7 && size <= 11 ) +// { +// if ( size == 9 ) +// { +// if ( *body == 'm' ) +// { +// if ( MORK_MEMCMP(body, "mork:none", 9) == 0 ) +// mStore_MorkNoneToken = inAtom->mBookAtom_Id; +// } +// else if ( *body == 'a' ) +// { +// if ( MORK_MEMCMP(body, "atomScope", 9) == 0 ) +// mStore_AtomScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( *body == 't' ) +// { +// if ( MORK_MEMCMP(body, "tableKind", 9) == 0 ) +// mStore_TableKindToken = inAtom->mBookAtom_Id; +// } +// } +// else if ( size == 7 && *body == 'c' ) +// { +// if ( MORK_MEMCMP(body, "charset", 7) == 0 ) +// mStore_CharsetToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 8 && *body == 'r' ) +// { +// if ( MORK_MEMCMP(body, "rowScope", 8) == 0 ) +// mStore_RowScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 10 && *body == 't' ) +// { +// if ( MORK_MEMCMP(body, "tableScope", 10) == 0 ) +// mStore_TableScopeToken = inAtom->mBookAtom_Id; +// } +// else if ( size == 11 && *body == 'c' ) +// { +// if ( MORK_MEMCMP(body, "columnScope", 11) == 0 ) +// mStore_ColumnScopeToken = inAtom->mBookAtom_Id; +// } +// } +// } +// } + +morkAtom* +morkStore::AddAlias(morkEnv* ev, const morkMid& inMid, mork_cscode inForm) +{ + morkBookAtom* outAtom = 0; + if ( ev->Good() ) + { + const mdbOid* oid = &inMid.mMid_Oid; + morkAtomSpace* atomSpace = this->LazyGetAtomSpace(ev, oid->mOid_Scope); + if ( atomSpace ) + { + morkFarBookAtom* keyAtom = + this->StageAliasAsFarBookAtom(ev, &inMid, atomSpace, inForm); + if ( keyAtom ) + { + morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids; + outAtom = map->GetAid(ev, (mork_aid) oid->mOid_Id); + if ( outAtom ) + { + if ( !outAtom->EqualFormAndBody(ev, keyAtom) ) + ev->NewError("duplicate alias ID"); + } + else + { + this->MaybeDirtyStore(); + keyAtom->mBookAtom_Id = oid->mOid_Id; + outAtom = atomSpace->MakeBookAtomCopyWithAid(ev, + *keyAtom, (mork_aid) oid->mOid_Id); + + // if ( outAtom && outAtom->IsWeeBook() ) + // { + // if ( oid->mOid_Scope == morkStore_kColumnSpaceScope ) + // { + // mork_size size = outAtom->mAtom_Size; + // if ( size >= 7 && size <= 11 ) + // this->SyncTokenIdChange(ev, outAtom, oid); + // } + // } + } + } + } + } + return outAtom; +} + +#define morkStore_kMaxCopyTokenSize 512 /* if larger, cannot be copied */ + +mork_token +morkStore::CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore) +// copy inToken from inStore over to this store +{ + mork_token outToken = 0; + if ( inStore == this ) // same store? + outToken = inToken; // just return token unchanged + else + { + char yarnBuf[ morkStore_kMaxCopyTokenSize ]; + mdbYarn yarn; + yarn.mYarn_Buf = yarnBuf; + yarn.mYarn_Fill = 0; + yarn.mYarn_Size = morkStore_kMaxCopyTokenSize; + yarn.mYarn_More = 0; + yarn.mYarn_Form = 0; + yarn.mYarn_Grow = 0; + + inStore->TokenToString(ev, inToken, &yarn); + if ( ev->Good() ) + { + morkBuf buf(yarn.mYarn_Buf, yarn.mYarn_Fill); + outToken = this->BufToToken(ev, &buf); + } + } + return outToken; +} + +mork_token +morkStore::BufToToken(morkEnv* ev, const morkBuf* inBuf) +{ + mork_token outToken = 0; + if ( ev->Good() ) + { + const mork_u1* s = (const mork_u1*) inBuf->mBuf_Body; + mork_bool nonAscii = ( *s > 0x7F ); + mork_size length = inBuf->mBuf_Fill; + if ( nonAscii || length > 1 ) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* space = this->LazyGetGroundColumnSpace(ev); + if ( space ) + { + morkFarBookAtom* keyAtom = 0; + if ( length <= morkBookAtom_kMaxBodySize ) + { + mork_aid aid = 1; // dummy + //mStore_BookAtom.InitMaxBookAtom(ev, *inBuf, form, space, aid); + mStore_FarBookAtom.InitFarBookAtom(ev, *inBuf, form, space, aid); + keyAtom = &mStore_FarBookAtom; + } + if ( keyAtom ) + { + morkAtomBodyMap* map = &space->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if ( bookAtom ) + outToken = bookAtom->mBookAtom_Id; + else + { + this->MaybeDirtyStore(); + bookAtom = space->MakeBookAtomCopy(ev, *keyAtom); + if ( bookAtom ) + { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } + else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_token +morkStore::StringToToken(morkEnv* ev, const char* inTokenName) +{ + mork_token outToken = 0; + if ( ev->Good() ) + { + const mork_u1* s = (const mork_u1*) inTokenName; + mork_bool nonAscii = ( *s > 0x7F ); + if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if ( groundSpace ) + { + morkFarBookAtom* keyAtom = + this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace); + if ( keyAtom ) + { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if ( bookAtom ) + outToken = bookAtom->mBookAtom_Id; + else + { + this->MaybeDirtyStore(); + bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom); + if ( bookAtom ) + { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + } + else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_token +morkStore::QueryToken(morkEnv* ev, const char* inTokenName) +{ + mork_token outToken = 0; + if ( ev->Good() ) + { + const mork_u1* s = (const mork_u1*) inTokenName; + mork_bool nonAscii = ( *s > 0x7F ); + if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte? + { + mork_cscode form = 0; // default charset + morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev); + if ( groundSpace ) + { + morkFarBookAtom* keyAtom = + this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace); + if ( keyAtom ) + { + morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies; + morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom); + if ( bookAtom ) + { + outToken = bookAtom->mBookAtom_Id; + bookAtom->MakeCellUseForever(ev); + } + } + } + } + else // only a single byte in inTokenName string: + outToken = *s; + } + + return outToken; +} + +mork_bool +morkStore::HasTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_count* outTableCount) +{ + MORK_USED_2(inRowScope,inTableKind); + mork_bool outBool = morkBool_kFalse; + mdb_count tableCount = 0; + + ev->StubMethodOnlyError(); + + if ( outTableCount ) + *outTableCount = tableCount; + return outBool; +} + +morkTable* +morkStore::GetTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_count* outTableCount, + mdb_bool* outMustBeUnique) +{ + morkTable* outTable = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if ( rowSpace ) + { + outTable = rowSpace->FindTableByKind(ev, inTableKind); + if ( outTable ) + { + if ( outTableCount ) + *outTableCount = outTable->GetRowCount(); + if ( outMustBeUnique ) + *outMustBeUnique = outTable->IsTableUnique(); + } + } + } + return outTable; +} + +morkRow* +morkStore::FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn, + const mdbYarn* inYarn) +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inScope); + if ( rowSpace ) + { + outRow = rowSpace->FindRow(ev, inColumn, inYarn); + } + } + return outRow; +} + +morkRow* +morkStore::GetRow(morkEnv* ev, const mdbOid* inOid) +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if ( rowSpace ) + { + outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid); + } + } + return outRow; +} + +morkTable* +morkStore::GetTable(morkEnv* ev, const mdbOid* inOid) +{ + morkTable* outTable = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if ( rowSpace ) + { + outTable = rowSpace->FindTableByTid(ev, inOid->mOid_Id); + } + } + return outTable; +} + +morkTable* +morkStore::NewTable(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying +{ + morkTable* outTable = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if ( rowSpace ) + outTable = rowSpace->NewTable(ev, inTableKind, inMustBeUnique, + inOptionalMetaRowOid); + } + return outTable; +} + +morkPortTableCursor* +morkStore::GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind) +{ + morkPortTableCursor* outCursor = 0; + if ( ev->Good() ) + { + nsIMdbHeap* heap = mPort_Heap; + outCursor = new(*heap, ev) + morkPortTableCursor(ev, morkUsage::kHeap, heap, this, + inRowScope, inTableKind, heap); + } + NS_IF_ADDREF(outCursor); + return outCursor; +} + +morkRow* +morkStore::NewRow(morkEnv* ev, mdb_scope inRowScope) +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope); + if ( rowSpace ) + outRow = rowSpace->NewRow(ev); + } + return outRow; +} + +morkRow* +morkStore::NewRowWithOid(morkEnv* ev, const mdbOid* inOid) +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if ( rowSpace ) + outRow = rowSpace->NewRowWithOid(ev, inOid); + } + return outRow; +} + +morkRow* +morkStore::OidToRow(morkEnv* ev, const mdbOid* inOid) + // OidToRow() finds old row with oid, or makes new one if not found. +{ + morkRow* outRow = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if ( rowSpace ) + { + outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid); + if ( !outRow && ev->Good() ) + outRow = rowSpace->NewRowWithOid(ev, inOid); + } + } + return outRow; +} + +morkTable* +morkStore::OidToTable(morkEnv* ev, const mdbOid* inOid, + const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying + // OidToTable() finds old table with oid, or makes new one if not found. +{ + morkTable* outTable = 0; + if ( ev->Good() ) + { + morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope); + if ( rowSpace ) + { + outTable = rowSpace->mRowSpace_Tables.GetTable(ev, inOid->mOid_Id); + if ( !outTable && ev->Good() ) + { + mork_kind tableKind = morkStore_kNoneToken; + outTable = rowSpace->NewTableWithTid(ev, inOid->mOid_Id, tableKind, + inOptionalMetaRowOid); + } + } + } + return outTable; +} + +// { ===== begin nsIMdbObject methods ===== + +// { ----- begin ref counting for well-behaved cyclic graphs ----- +NS_IMETHODIMP +morkStore::GetWeakRefCount(nsIMdbEnv* mev, // weak refs + mdb_count* outCount) +{ + *outCount = WeakRefsOnly(); + return NS_OK; +} +NS_IMETHODIMP +morkStore::GetStrongRefCount(nsIMdbEnv* mev, // strong refs + mdb_count* outCount) +{ + *outCount = StrongRefsOnly(); + return NS_OK; +} +// ### TODO - clean up this cast, if required +NS_IMETHODIMP +morkStore::AddWeakRef(nsIMdbEnv* mev) +{ + morkEnv *ev = morkEnv::FromMdbEnv(mev); + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::AddWeakRef(ev)); +} +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkStore::AddStrongRef(morkEnv* mev) +{ + return AddRef(); +} +#endif +NS_IMETHODIMP_(mork_uses) +morkStore::AddStrongRef(nsIMdbEnv* mev) +{ + return AddRef(); +} +NS_IMETHODIMP +morkStore::CutWeakRef(nsIMdbEnv* mev) +{ + morkEnv *ev = morkEnv::FromMdbEnv(mev); + // XXX Casting mork_refs to nsresult + return static_cast(morkNode::CutWeakRef(ev)); +} +#ifndef _MSC_VER +NS_IMETHODIMP_(mork_uses) +morkStore::CutStrongRef(morkEnv* mev) +{ + return Release(); +} +#endif +NS_IMETHODIMP +morkStore::CutStrongRef(nsIMdbEnv* mev) +{ + // XXX Casting nsrefcnt to nsresult + return static_cast(Release()); +} + +NS_IMETHODIMP +morkStore::CloseMdbObject(nsIMdbEnv* mev) +{ + morkEnv *ev = morkEnv::FromMdbEnv(mev); + CloseMorkNode(ev); + Release(); + return NS_OK; +} + +NS_IMETHODIMP +morkStore::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen) +{ + *outOpen = IsOpenNode(); + return NS_OK; +} +// } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + +// { ===== begin nsIMdbPort methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkStore::GetIsPortReadonly(nsIMdbEnv* mev, mdb_bool* outBool) +{ + nsresult outErr = NS_OK; + mdb_bool isReadOnly = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if ( outBool ) + *outBool = isReadOnly; + return outErr; +} + +morkEnv* +morkStore::CanUseStore(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const +{ + morkEnv* outEnv = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if (IsStore()) + outEnv = ev; + else + NonStoreTypeError(ev); + *outErr = ev->AsErr(); + } + MORK_ASSERT(outEnv); + return outEnv; +} + +NS_IMETHODIMP +morkStore::GetIsStore(nsIMdbEnv* mev, mdb_bool* outBool) +{ + MORK_USED_1(mev); + if ( outBool ) + *outBool = morkBool_kTrue; + return NS_OK; +} + +NS_IMETHODIMP +morkStore::GetIsStoreAndDirty(nsIMdbEnv* mev, mdb_bool* outBool) +{ + nsresult outErr = NS_OK; + mdb_bool isStoreAndDirty = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if ( outBool ) + *outBool = isStoreAndDirty; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetUsagePolicy(nsIMdbEnv* mev, + mdbUsagePolicy* ioUsagePolicy) +{ + MORK_USED_1(ioUsagePolicy); + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::SetUsagePolicy(nsIMdbEnv* mev, + const mdbUsagePolicy* inUsagePolicy) +{ + MORK_USED_1(inUsagePolicy); + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin memory policy methods ----- +NS_IMETHODIMP +morkStore::IdleMemoryPurge( // do memory management already scheduled + nsIMdbEnv* mev, // context + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + nsresult outErr = NS_OK; + mdb_size estimatedBytesFreed = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if ( outEstimatedBytesFreed ) + *outEstimatedBytesFreed = estimatedBytesFreed; + return outErr; +} + +NS_IMETHODIMP +morkStore::SessionMemoryPurge( // request specific footprint decrease + nsIMdbEnv* mev, // context + mdb_size inDesiredBytesFreed, // approximate number of bytes wanted + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + MORK_USED_1(inDesiredBytesFreed); + nsresult outErr = NS_OK; + mdb_size estimate = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if ( outEstimatedBytesFreed ) + *outEstimatedBytesFreed = estimate; + return outErr; +} + +NS_IMETHODIMP +morkStore::PanicMemoryPurge( // desperately free all possible memory + nsIMdbEnv* mev, // context + mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed +{ + nsresult outErr = NS_OK; + mdb_size estimate = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing? + outErr = ev->AsErr(); + } + if ( outEstimatedBytesFreed ) + *outEstimatedBytesFreed = estimate; + return outErr; +} +// } ----- end memory policy methods ----- + +// { ----- begin filepath methods ----- +NS_IMETHODIMP +morkStore::GetPortFilePath( + nsIMdbEnv* mev, // context + mdbYarn* outFilePath, // name of file holding port content + mdbYarn* outFormatVersion) // file format description +{ + nsresult outErr = NS_OK; + if ( outFormatVersion ) + outFormatVersion->mYarn_Fill = 0; + if ( outFilePath ) + outFilePath->mYarn_Fill = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + if ( mStore_File ) + mStore_File->Path(mev, outFilePath); + else + NilStoreFileError(ev); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::GetPortFile( + nsIMdbEnv* mev, // context + nsIMdbFile** acqFile) // acquire file used by port or store +{ + nsresult outErr = NS_OK; + if ( acqFile ) + *acqFile = 0; + + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + + if ( mStore_File ) + { + if ( acqFile ) + { + mStore_File->AddRef(); + if ( ev->Good() ) + *acqFile = mStore_File; + } + } + else + NilStoreFileError(ev); + + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end filepath methods ----- + +// { ----- begin export methods ----- +NS_IMETHODIMP +morkStore::BestExportFormat( // determine preferred export format + nsIMdbEnv* mev, // context + mdbYarn* outFormatVersion) // file format description +{ + nsresult outErr = NS_OK; + if ( outFormatVersion ) + outFormatVersion->mYarn_Fill = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::CanExportToFormat( // can export content in given specific format? + nsIMdbEnv* mev, // context + const char* inFormatVersion, // file format description + mdb_bool* outCanExport) // whether ExportSource() might succeed +{ + MORK_USED_1(inFormatVersion); + mdb_bool canExport = morkBool_kFalse; + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + ev->StubMethodOnlyError(); + outErr = ev->AsErr(); + } + if ( outCanExport ) + *outCanExport = canExport; + return outErr; +} + +NS_IMETHODIMP +morkStore::ExportToFormat( // export content in given specific format + nsIMdbEnv* mev, // context + // const char* inFilePath, // the file to receive exported content + nsIMdbFile* ioFile, // destination abstract file interface + const char* inFormatVersion, // file format description + nsIMdbThumb** acqThumb) // acquire thumb for incremental export +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the export will be finished. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + if ( ioFile && inFormatVersion && acqThumb ) + { + ev->StubMethodOnlyError(); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} + +// } ----- end export methods ----- + +// { ----- begin token methods ----- +NS_IMETHODIMP +morkStore::TokenToString( // return a string name for an integer token + nsIMdbEnv* mev, // context + mdb_token inToken, // token for inTokenName inside this port + mdbYarn* outTokenName) // the type of table to access +{ + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + TokenToString(ev, inToken, outTokenName); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::StringToToken( // return an integer token for scope name + nsIMdbEnv* mev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) // token for inTokenName inside this port + // String token zero is never used and never supported. If the port + // is a mutable store, then StringToToken() to create a new + // association of inTokenName with a new integer token if possible. + // But a readonly port will return zero for an unknown scope name. +{ + nsresult outErr = NS_OK; + mdb_token token = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + token = StringToToken(ev, inTokenName); + outErr = ev->AsErr(); + } + if ( outToken ) + *outToken = token; + return outErr; +} + + +NS_IMETHODIMP +morkStore::QueryToken( // like StringToToken(), but without adding + nsIMdbEnv* mev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) // token for inTokenName inside this port + // QueryToken() will return a string token if one already exists, + // but unlike StringToToken(), will not assign a new token if not + // already in use. +{ + nsresult outErr = NS_OK; + mdb_token token = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + token = QueryToken(ev, inTokenName); + outErr = ev->AsErr(); + } + if ( outToken ) + *outToken = token; + return outErr; +} + + +// } ----- end token methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkStore::HasRow( // contains a row with the specified oid? + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_bool* outHasRow) // whether GetRow() might succeed +{ + nsresult outErr = NS_OK; + mdb_bool hasRow = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = GetRow(ev, inOid); + if ( row ) + hasRow = morkBool_kTrue; + + outErr = ev->AsErr(); + } + if ( outHasRow ) + *outHasRow = hasRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetRow( // access one row with specific oid + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + nsIMdbRow** acqRow) // acquire specific row (or null) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = GetRow(ev, inOid); + if ( row && ev->Good() ) + outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetRowRefCount( // get number of tables that contain a row + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_count* outRefCount) // number of tables containing inRowKey +{ + nsresult outErr = NS_OK; + mdb_count count = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = GetRow(ev, inOid); + if ( row && ev->Good() ) + count = row->mRow_GcUses; + + outErr = ev->AsErr(); + } + if ( outRefCount ) + *outRefCount = count; + return outErr; +} + +NS_IMETHODIMP +morkStore::FindRow(nsIMdbEnv* mev, // search for row with matching cell + mdb_scope inRowScope, // row scope for row ids + mdb_column inColumn, // the column to search (and maintain an index) + const mdbYarn* inTargetCellValue, // cell value for which to search + mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match) + nsIMdbRow** acqRow) // acquire matching row (or nil for no match) + // FindRow() searches for one row that has a cell in column inColumn with + // a contained value with the same form (i.e. charset) and is byte-wise + // identical to the blob described by yarn inTargetCellValue. Both content + // and form of the yarn must be an exact match to find a matching row. + // + // (In other words, both a yarn's blob bytes and form are significant. The + // form is not expected to vary in columns used for identity anyway. This + // is intended to make the cost of FindRow() cheaper for MDB implementors, + // since any cell value atomization performed internally must necessarily + // make yarn form significant in order to avoid data loss in atomization.) + // + // FindRow() can lazily create an index on attribute inColumn for all rows + // with that attribute in row space scope inRowScope, so that subsequent + // calls to FindRow() will perform faster. Such an index might or might + // not be persistent (but this seems desirable if it is cheap to do so). + // Note that lazy index creation in readonly DBs is not very feasible. + // + // This FindRow() interface assumes that attribute inColumn is effectively + // an alternative means of unique identification for a row in a rowspace, + // so correct behavior is only guaranteed when no duplicates for this col + // appear in the given set of rows. (If more than one row has the same cell + // value in this column, no more than one will be found; and cutting one of + // two duplicate rows can cause the index to assume no other such row lives + // in the row space, so future calls return nil for negative search results + // even though some duplicate row might still live within the rowspace.) + // + // In other words, the FindRow() implementation is allowed to assume simple + // hash tables mapping unqiue column keys to associated row values will be + // sufficient, where any duplication is not recorded because only one copy + // of a given key need be remembered. Implementors are not required to sort + // all rows by the specified column. +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + mdbOid rowOid; + rowOid.mOid_Scope = 0; + rowOid.mOid_Id = (mdb_id) -1; + + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = FindRow(ev, inRowScope, inColumn, inTargetCellValue); + if ( row && ev->Good() ) + { + rowOid = row->mRow_Oid; + if ( acqRow ) + outRow = row->AcquireRowHandle(ev, this); + } + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + if ( outRowOid ) + *outRowOid = rowOid; + + return outErr; +} + +// } ----- end row methods ----- + +// { ----- begin table methods ----- +NS_IMETHODIMP +morkStore::HasTable( // supports a table with the specified oid? + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical table oid + mdb_bool* outHasTable) // whether GetTable() might succeed +{ + nsresult outErr = NS_OK; + mork_bool hasTable = morkBool_kFalse; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = GetTable(ev, inOid); + if ( table ) + hasTable = morkBool_kTrue; + + outErr = ev->AsErr(); + } + if ( outHasTable ) + *outHasTable = hasTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetTable( // access one table with specific oid + nsIMdbEnv* mev, // context + const mdbOid* inOid, // hypothetical table oid + nsIMdbTable** acqTable) // acquire specific table (or null) +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = GetTable(ev, inOid); + if ( table && ev->Good() ) + outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::HasTableKind( // supports a table of the specified type? + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // rid scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outSupportsTable) // whether GetTableKind() might succeed +{ + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + *outSupportsTable = HasTableKind(ev, inRowScope, + inTableKind, outTableCount); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::GetTableKind( // access one (random) table of specific type + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outMustBeUnique, // whether port can hold only one of these + nsIMdbTable** acqTable) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = GetTableKind(ev, inRowScope, + inTableKind, outTableCount, outMustBeUnique); + if ( table && ev->Good() ) + outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::GetPortTableCursor( // get cursor for all tables of specific type + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + nsIMdbPortTableCursor** acqCursor) // all such tables in the port +{ + nsresult outErr = NS_OK; + nsIMdbPortTableCursor* outCursor = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkPortTableCursor* cursor = + GetPortTableCursor(ev, inRowScope, + inTableKind); + if ( cursor && ev->Good() ) + outCursor = cursor; + + outErr = ev->AsErr(); + } + if ( acqCursor ) + *acqCursor = outCursor; + return outErr; +} +// } ----- end table methods ----- + +// { ----- begin commit methods ----- + +NS_IMETHODIMP +morkStore::ShouldCompress( // store wastes at least inPercentWaste? + nsIMdbEnv* mev, // context + mdb_percent inPercentWaste, // 0..100 percent file size waste threshold + mdb_percent* outActualWaste, // 0..100 percent of file actually wasted + mdb_bool* outShould) // true when about inPercentWaste% is wasted +{ + mdb_percent actualWaste = 0; + mdb_bool shouldCompress = morkBool_kFalse; + nsresult outErr = NS_OK; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + actualWaste = PercentOfStoreWasted(ev); + if ( inPercentWaste > 100 ) + inPercentWaste = 100; + shouldCompress = ( actualWaste >= inPercentWaste ); + outErr = ev->AsErr(); + } + if ( outActualWaste ) + *outActualWaste = actualWaste; + if ( outShould ) + *outShould = shouldCompress; + return outErr; +} + + +// } ===== end nsIMdbPort methods ===== + +NS_IMETHODIMP +morkStore::NewTable( // make one new table of specific type + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = NewTable(ev, inRowScope, + inTableKind, inMustBeUnique, inOptionalMetaRowOid); + if ( table && ev->Good() ) + outTable = table->AcquireTableHandle(ev); + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} + +NS_IMETHODIMP +morkStore::NewTableWithOid( // make one new table of specific type + nsIMdbEnv* mev, // context + const mdbOid* inOid, // caller assigned oid + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) // acquire scoped collection of rows +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkTable* table = OidToTable(ev, inOid, + inOptionalMetaRowOid); + if ( table && ev->Good() ) + { + table->mTable_Kind = inTableKind; + if ( inMustBeUnique ) + table->SetTableUnique(); + outTable = table->AcquireTableHandle(ev); + } + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} +// } ----- end table methods ----- + +// { ----- begin row scope methods ----- +NS_IMETHODIMP +morkStore::RowScopeHasAssignedIds(nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::SetCallerAssignedIds(nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::SetStoreAssignedIds(nsIMdbEnv* mev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) // nonzero if store db assigned specified +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end row scope methods ----- + +// { ----- begin row methods ----- +NS_IMETHODIMP +morkStore::NewRowWithOid(nsIMdbEnv* mev, // new row w/ caller assigned oid + const mdbOid* inOid, // caller assigned oid + nsIMdbRow** acqRow) // create new row +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = NewRowWithOid(ev, inOid); + if ( row && ev->Good() ) + outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkStore::NewRow(nsIMdbEnv* mev, // new row with db assigned oid + mdb_scope inRowScope, // row scope for row ids + nsIMdbRow** acqRow) // create new row +// Note this row must be added to some table or cell child before the +// store is closed in order to make this row persist across sesssions. +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + morkRow* row = NewRow(ev, inRowScope); + if ( row && ev->Good() ) + outRow = row->AcquireRowHandle(ev, this); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} +// } ----- end row methods ----- + +// { ----- begin inport/export methods ----- +NS_IMETHODIMP +morkStore::ImportContent( // import content from port + nsIMdbEnv* mev, // context + mdb_scope inRowScope, // scope for rows (or zero for all?) + nsIMdbPort* ioPort, // the port with content to add to store + nsIMdbThumb** acqThumb) // acquire thumb for incremental import +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the import will be finished. +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStore::ImportFile( // import content from port + nsIMdbEnv* mev, // context + nsIMdbFile* ioFile, // the file with content to add to store + nsIMdbThumb** acqThumb) // acquire thumb for incremental import +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the import will be finished. +{ + NS_ASSERTION(false, " not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end inport/export methods ----- + +// { ----- begin hinting methods ----- +NS_IMETHODIMP +morkStore::ShareAtomColumnsHint( // advise re shared col content atomizing + nsIMdbEnv* mev, // context + mdb_scope inScopeHint, // zero, or suggested shared namespace + const mdbColumnSet* inColumnSet) // cols desired tokenized together +{ + MORK_USED_2(inColumnSet,inScopeHint); + nsresult outErr = NS_OK; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing for a hint method + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkStore::AvoidAtomColumnsHint( // advise col w/ poor atomizing prospects + nsIMdbEnv* mev, // context + const mdbColumnSet* inColumnSet) // cols with poor atomizing prospects +{ + MORK_USED_1(inColumnSet); + nsresult outErr = NS_OK; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + // ev->StubMethodOnlyError(); // okay to do nothing for a hint method + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end hinting methods ----- + +// { ----- begin commit methods ----- +NS_IMETHODIMP +morkStore::LargeCommit( // save important changes if at all possible + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // acquire thumb for incremental commit +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the commit will be finished. Note the store is effectively write +// locked until commit is finished or canceled through the thumb instance. +// Until the commit is done, the store will report it has readonly status. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + + morkThumb* thumb = 0; + // morkFile* file = store->mStore_File; + if ( DoPreferLargeOverCompressCommit(ev) ) + { + thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this); + } + else + { + mork_bool doCollect = morkBool_kFalse; + thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + } + + if ( thumb ) + { + outThumb = thumb; + thumb->AddRef(); + } + + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} + +NS_IMETHODIMP +morkStore::SessionCommit( // save all changes if large commits delayed + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // acquire thumb for incremental commit +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the commit will be finished. Note the store is effectively write +// locked until commit is finished or canceled through the thumb instance. +// Until the commit is done, the store will report it has readonly status. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + + morkThumb* thumb = 0; + if ( DoPreferLargeOverCompressCommit(ev) ) + { + thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this); + } + else + { + mork_bool doCollect = morkBool_kFalse; + thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + } + + if ( thumb ) + { + outThumb = thumb; + thumb->AddRef(); + } + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} + +NS_IMETHODIMP +morkStore::CompressCommit( // commit and make db smaller if possible + nsIMdbEnv* mev, // context + nsIMdbThumb** acqThumb) // acquire thumb for incremental commit +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the commit will be finished. Note the store is effectively write +// locked until commit is finished or canceled through the thumb instance. +// Until the commit is done, the store will report it has readonly status. +{ + nsresult outErr = NS_OK; + nsIMdbThumb* outThumb = 0; + morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr); + if ( ev ) + { + mork_bool doCollect = morkBool_kFalse; + morkThumb* thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect); + if ( thumb ) + { + outThumb = thumb; + thumb->AddRef(); + mStore_CanWriteIncremental = morkBool_kTrue; + } + + outErr = ev->AsErr(); + } + if ( acqThumb ) + *acqThumb = outThumb; + return outErr; +} + +// } ----- end commit methods ----- + +// } ===== end nsIMdbStore methods ===== + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkStore.h b/db/mork/src/morkStore.h new file mode 100644 index 0000000000..f0471ff15c --- /dev/null +++ b/db/mork/src/morkStore.h @@ -0,0 +1,768 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKSTORE_ +#define _MORKSTORE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +#ifndef _MORKPOOL_ +#include "morkPool.h" +#endif + +#ifndef _MORKZONE_ +#include "morkZone.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kPort /*i*/ 0x7054 /* ascii 'pT' */ + +#define morkDerived_kStore /*i*/ 0x7354 /* ascii 'sT' */ + +/*| kGroundColumnSpace: we use the 'column space' as the default scope +**| for grounding column name IDs, and this is also the default scope for +**| all other explicitly tokenized strings. +|*/ +#define morkStore_kGroundColumnSpace 'c' /* for mStore_GroundColumnSpace*/ +#define morkStore_kColumnSpaceScope ((mork_scope) 'c') /*kGroundColumnSpace*/ +#define morkStore_kValueSpaceScope ((mork_scope) 'v') +#define morkStore_kStreamBufSize (8 * 1024) /* okay buffer size */ + +#define morkStore_kReservedColumnCount 0x20 /* for well-known columns */ + +#define morkStore_kNoneToken ((mork_token) 'n') +#define morkStore_kFormColumn ((mork_column) 'f') +#define morkStore_kAtomScopeColumn ((mork_column) 'a') +#define morkStore_kRowScopeColumn ((mork_column) 'r') +#define morkStore_kMetaScope ((mork_scope) 'm') +#define morkStore_kKindColumn ((mork_column) 'k') +#define morkStore_kStatusColumn ((mork_column) 's') + +/*| morkStore: +|*/ +class morkStore : public morkObject, public nsIMdbStore { + +public: // state is public because the entire Mork system is private + + NS_DECL_ISUPPORTS_INHERITED + + morkEnv* mPort_Env; // non-refcounted env which created port + morkFactory* mPort_Factory; // weak ref to suite factory + nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + + void ClosePort(morkEnv* ev); // called by CloseMorkNode(); + +public: // dynamic type identification + mork_bool IsPort() const + { return IsNode() && mNode_Derived == morkDerived_kPort; } +// } ===== end morkNode methods ===== + +public: // other port methods + + // { ----- begin attribute methods ----- +// NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly); + // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port. + // } ----- end attribute methods ----- + + // { ----- begin factory methods ----- +// NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory); + // } ----- end factory methods ----- + + // { ----- begin ref counting for well-behaved cyclic graphs ----- + NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs + mdb_count* outCount) override; + NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs + mdb_count* outCount) override; + + NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override; +#endif + NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override; + + NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero + NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override; + // } ----- end ref counting ----- + +// } ===== end nsIMdbObject methods ===== + +// { ===== begin nsIMdbPort methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) override; + NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) override; + NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) override; + + NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev, + mdbUsagePolicy* ioUsagePolicy) override; + + NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev, + const mdbUsagePolicy* inUsagePolicy) override; + // } ----- end attribute methods ----- + + // { ----- begin memory policy methods ----- + NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed + + NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease + nsIMdbEnv* ev, // context + mdb_size inDesiredBytesFreed, // approximate number of bytes wanted + mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed + + NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory + nsIMdbEnv* ev, // context + mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed + // } ----- end memory policy methods ----- + + // { ----- begin filepath methods ----- + NS_IMETHOD GetPortFilePath( + nsIMdbEnv* ev, // context + mdbYarn* outFilePath, // name of file holding port content + mdbYarn* outFormatVersion) override; // file format description + + NS_IMETHOD GetPortFile( + nsIMdbEnv* ev, // context + nsIMdbFile** acqFile) override; // acquire file used by port or store + // } ----- end filepath methods ----- + + // { ----- begin export methods ----- + NS_IMETHOD BestExportFormat( // determine preferred export format + nsIMdbEnv* ev, // context + mdbYarn* outFormatVersion) override; // file format description + + NS_IMETHOD + CanExportToFormat( // can export content in given specific format? + nsIMdbEnv* ev, // context + const char* inFormatVersion, // file format description + mdb_bool* outCanExport) override; // whether ExportSource() might succeed + + NS_IMETHOD ExportToFormat( // export content in given specific format + nsIMdbEnv* ev, // context + // const char* inFilePath, // the file to receive exported content + nsIMdbFile* ioFile, // destination abstract file interface + const char* inFormatVersion, // file format description + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental export + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the export will be finished. + + // } ----- end export methods ----- + + // { ----- begin token methods ----- + NS_IMETHOD TokenToString( // return a string name for an integer token + nsIMdbEnv* ev, // context + mdb_token inToken, // token for inTokenName inside this port + mdbYarn* outTokenName) override; // the type of table to access + + NS_IMETHOD StringToToken( // return an integer token for scope name + nsIMdbEnv* ev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) override; // token for inTokenName inside this port + + // String token zero is never used and never supported. If the port + // is a mutable store, then StringToToken() to create a new + // association of inTokenName with a new integer token if possible. + // But a readonly port will return zero for an unknown scope name. + + NS_IMETHOD QueryToken( // like StringToToken(), but without adding + nsIMdbEnv* ev, // context + const char* inTokenName, // Latin1 string to tokenize if possible + mdb_token* outToken) override; // token for inTokenName inside this port + + // QueryToken() will return a string token if one already exists, + // but unlike StringToToken(), will not assign a new token if not + // already in use. + + // } ----- end token methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD HasRow( // contains a row with the specified oid? + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_bool* outHasRow) override; // whether GetRow() might succeed + + NS_IMETHOD GetRowRefCount( // get number of tables that contain a row + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + mdb_count* outRefCount) override; // number of tables containing inRowKey + + NS_IMETHOD GetRow( // access one row with specific oid + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical row oid + nsIMdbRow** acqRow) override; // acquire specific row (or null) + + NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell + mdb_scope inRowScope, // row scope for row ids + mdb_column inColumn, // the column to search (and maintain an index) + const mdbYarn* inTargetCellValue, // cell value for which to search + mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match) + nsIMdbRow** acqRow) override; // acquire matching row (or nil for no match) + // can be null if you only want the oid + // FindRow() searches for one row that has a cell in column inColumn with + // a contained value with the same form (i.e. charset) and is byte-wise + // identical to the blob described by yarn inTargetCellValue. Both content + // and form of the yarn must be an exact match to find a matching row. + // + // (In other words, both a yarn's blob bytes and form are significant. The + // form is not expected to vary in columns used for identity anyway. This + // is intended to make the cost of FindRow() cheaper for MDB implementors, + // since any cell value atomization performed internally must necessarily + // make yarn form significant in order to avoid data loss in atomization.) + // + // FindRow() can lazily create an index on attribute inColumn for all rows + // with that attribute in row space scope inRowScope, so that subsequent + // calls to FindRow() will perform faster. Such an index might or might + // not be persistent (but this seems desirable if it is cheap to do so). + // Note that lazy index creation in readonly DBs is not very feasible. + // + // This FindRow() interface assumes that attribute inColumn is effectively + // an alternative means of unique identification for a row in a rowspace, + // so correct behavior is only guaranteed when no duplicates for this col + // appear in the given set of rows. (If more than one row has the same cell + // value in this column, no more than one will be found; and cutting one of + // two duplicate rows can cause the index to assume no other such row lives + // in the row space, so future calls return nil for negative search results + // even though some duplicate row might still live within the rowspace.) + // + // In other words, the FindRow() implementation is allowed to assume simple + // hash tables mapping unqiue column keys to associated row values will be + // sufficient, where any duplication is not recorded because only one copy + // of a given key need be remembered. Implementors are not required to sort + // all rows by the specified column. + // } ----- end row methods ----- + + // { ----- begin table methods ----- + NS_IMETHOD HasTable( // supports a table with the specified oid? + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical table oid + mdb_bool* outHasTable) override; // whether GetTable() might succeed + + NS_IMETHOD GetTable( // access one table with specific oid + nsIMdbEnv* ev, // context + const mdbOid* inOid, // hypothetical table oid + nsIMdbTable** acqTable) override; // acquire specific table (or null) + + NS_IMETHOD HasTableKind( // supports a table of the specified type? + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // rid scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outSupportsTable) override; // whether GetTableKind() might succeed + + NS_IMETHOD GetTableKind( // access one (random) table of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_count* outTableCount, // current number of such tables + mdb_bool* outMustBeUnique, // whether port can hold only one of these + nsIMdbTable** acqTable) override; // acquire scoped collection of rows + + NS_IMETHOD + GetPortTableCursor( // get cursor for all tables of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + nsIMdbPortTableCursor** acqCursor) override; // all such tables in the port + // } ----- end table methods ----- + + + // { ----- begin commit methods ----- + + NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste? + nsIMdbEnv* ev, // context + mdb_percent inPercentWaste, // 0..100 percent file size waste threshold + mdb_percent* outActualWaste, // 0..100 percent of file actually wasted + mdb_bool* outShould) override; // true when about inPercentWaste% is wasted + // ShouldCompress() returns true if the store can determine that the file + // will shrink by an estimated percentage of inPercentWaste% (or more) if + // CompressCommit() is called, because that percentage of the file seems + // to be recoverable free space. The granularity is only in terms of + // percentage points, and any value over 100 is considered equal to 100. + // + // If a store only has an approximate idea how much space might be saved + // during a compress, then a best guess should be made. For example, the + // Mork implementation might keep track of how much file space began with + // text content before the first updating transaction, and then consider + // all content following the start of the first transaction as potentially + // wasted space if it is all updates and not just new content. (This is + // a safe assumption in the sense that behavior will stabilize on a low + // estimate of wastage after a commit removes all transaction updates.) + // + // Some db formats might attempt to keep a very accurate reckoning of free + // space size, so a very accurate determination can be made. But other db + // formats might have difficulty determining size of free space, and might + // require some lengthy calculation to answer. This is the reason for + // passing in the percentage threshold of interest, so that such lengthy + // computations can terminate early as soon as at least inPercentWaste is + // found, so that the entire file need not be groveled when unnecessary. + // However, we hope implementations will always favor fast but imprecise + // heuristic answers instead of extremely slow but very precise answers. + // + // If the outActualWaste parameter is non-nil, it will be used to return + // the actual estimated space wasted as a percentage of file size. (This + // parameter is provided so callers need not call repeatedly with altered + // inPercentWaste values to isolate the actual wastage figure.) Note the + // actual wastage figure returned can exactly equal inPercentWaste even + // when this grossly underestimates the real figure involved, if the db + // finds it very expensive to determine the extent of wastage after it is + // known to at least exceed inPercentWaste. Note we expect that whenever + // outShould returns true, that outActualWaste returns >= inPercentWaste. + // + // The effect of different inPercentWaste values is not very uniform over + // the permitted range. For example, 50 represents 50% wastage, or a file + // that is about double what it should be ideally. But 99 represents 99% + // wastage, or a file that is about ninety-nine times as big as it should + // be ideally. In the smaller direction, 25 represents 25% wastage, or + // a file that is only 33% larger than it should be ideally. + // + // Callers can determine what policy they want to use for considering when + // a file holds too much wasted space, and express this as a percentage + // of total file size to pass as in the inPercentWaste parameter. A zero + // likely returns always trivially true, and 100 always trivially false. + // The great majority of callers are expected to use values from 25 to 75, + // since most plausible thresholds for compressing might fall between the + // extremes of 133% of ideal size and 400% of ideal size. (Presumably the + // larger a file gets, the more important the percentage waste involved, so + // a sliding scale for compress thresholds might use smaller numbers for + // much bigger file sizes.) + + // } ----- end commit methods ----- + +// } ===== end nsIMdbPort methods ===== + +// { ===== begin nsIMdbStore methods ===== + + // { ----- begin table methods ----- + NS_IMETHOD NewTable( // make one new table of specific type + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // row scope for row ids + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) override; // acquire scoped collection of rows + + NS_IMETHOD NewTableWithOid( // make one new table of specific type + nsIMdbEnv* ev, // context + const mdbOid* inOid, // caller assigned oid + mdb_kind inTableKind, // the type of table to access + mdb_bool inMustBeUnique, // whether store can hold only one of these + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + nsIMdbTable** acqTable) override; // acquire scoped collection of rows + // } ----- end table methods ----- + + // { ----- begin row scope methods ----- + NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified + + NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified + + NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev, + mdb_scope inRowScope, // row scope for row ids + mdb_bool* outCallerAssigned, // nonzero if caller assigned specified + mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified + // } ----- end row scope methods ----- + + // { ----- begin row methods ----- + NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid + const mdbOid* inOid, // caller assigned oid + nsIMdbRow** acqRow) override; // create new row + + NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid + mdb_scope inRowScope, // row scope for row ids + nsIMdbRow** acqRow) override; // create new row + // Note this row must be added to some table or cell child before the + // store is closed in order to make this row persist across sesssions. + + // } ----- end row methods ----- + + // { ----- begin inport/export methods ----- + NS_IMETHOD ImportContent( // import content from port + nsIMdbEnv* ev, // context + mdb_scope inRowScope, // scope for rows (or zero for all?) + nsIMdbPort* ioPort, // the port with content to add to store + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the import will be finished. + + NS_IMETHOD ImportFile( // import content from port + nsIMdbEnv* ev, // context + nsIMdbFile* ioFile, // the file with content to add to store + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the import will be finished. + // } ----- end inport/export methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD + ShareAtomColumnsHint( // advise re shared column content atomizing + nsIMdbEnv* ev, // context + mdb_scope inScopeHint, // zero, or suggested shared namespace + const mdbColumnSet* inColumnSet) override; // cols desired tokenized together + + NS_IMETHOD + AvoidAtomColumnsHint( // advise column with poor atomizing prospects + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) override; // cols with poor atomizing prospects + // } ----- end hinting methods ----- + + // { ----- begin commit methods ----- + NS_IMETHOD LargeCommit( // save important changes if at all possible + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + NS_IMETHOD SessionCommit( // save all changes if large commits delayed + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + NS_IMETHOD + CompressCommit( // commit and make db physically smaller if possible + nsIMdbEnv* ev, // context + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the commit will be finished. Note the store is effectively write + // locked until commit is finished or canceled through the thumb instance. + // Until the commit is done, the store will report it has readonly status. + + // } ----- end commit methods ----- + +// } ===== end nsIMdbStore methods ===== + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakPort(morkPort* me, + morkEnv* ev, morkPort** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongPort(morkPort* me, + morkEnv* ev, morkPort** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +// public: // slots inherited from morkPort (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkEnv* mPort_Env; // non-refcounted env which created port + // morkFactory* mPort_Factory; // weak ref to suite factory + // nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects + +public: // state is public because the entire Mork system is private + +// mStore_OidAtomSpace might be unnecessary; I don't remember why I wanted it. + morkAtomSpace* mStore_OidAtomSpace; // ground atom space for oids + morkAtomSpace* mStore_GroundAtomSpace; // ground atom space for scopes + morkAtomSpace* mStore_GroundColumnSpace; // ground column space for scopes + + nsIMdbFile* mStore_File; // the file containing Mork text + morkStream* mStore_InStream; // stream using file used by the builder + morkBuilder* mStore_Builder; // to parse Mork text and build structures + + morkStream* mStore_OutStream; // stream using file used by the writer + + morkRowSpaceMap mStore_RowSpaces; // maps mork_scope -> morkSpace + morkAtomSpaceMap mStore_AtomSpaces; // maps mork_scope -> morkSpace + + morkZone mStore_Zone; + + morkPool mStore_Pool; + + // we alloc a max size book atom to reuse space for atom map key searches: + // morkMaxBookAtom mStore_BookAtom; // staging area for atom map searches + + morkFarBookAtom mStore_FarBookAtom; // staging area for atom map searches + + // GroupIdentity should be one more than largest seen in a parsed db file: + mork_gid mStore_CommitGroupIdentity; // transaction ID number + + // group positions are used to help compute PercentOfStoreWasted(): + mork_pos mStore_FirstCommitGroupPos; // start of first group + mork_pos mStore_SecondCommitGroupPos; // start of second group + // If the first commit group is very near the start of the file (say less + // than 512 bytes), then we might assume the file started nearly empty and + // that most of the first group is not wasted. In that case, the pos of + // the second commit group might make a better estimate of the start of + // transaction space that might represent wasted file space. That's why + // we support fields for both first and second commit group positions. + // + // We assume that a zero in either group pos means that the slot has not + // yet been given a valid value, since the file will always start with a + // tag, and a commit group cannot actually start at position zero. + // + // Either or both the first or second commit group positions might be + // supplied by either morkWriter (while committing) or morkBuilder (while + // parsing), since either reading or writing the file might encounter the + // first transaction groups which came into existence either in the past + // or in the very recent present. + + mork_bool mStore_CanAutoAssignAtomIdentity; + mork_bool mStore_CanDirty; // changes imply the store becomes dirty? + mork_u1 mStore_CanWriteIncremental; // compress not required? + mork_u1 mStore_Pad; // for u4 alignment + + // mStore_CanDirty should be FALSE when parsing a file while building the + // content going into the store, because such data structure modifications + // are actuallly in sync with the file. So content read from a file must + // be clean with respect to the file. After a file is finished parsing, + // the mStore_CanDirty slot should become TRUE, so that any additional + // changes at runtime cause structures to be marked dirty with respect to + // the file which must later be updated with changes during a commit. + // + // It might also make sense to set mStore_CanDirty to FALSE while a commit + // is in progress, lest some internal transformations make more content + // appear dirty when it should not. So anyone modifying content during a + // commit should think about the intended significance regarding dirty. + +public: // more specific dirty methods for store: + void SetStoreDirty() { this->SetNodeDirty(); } + void SetStoreClean() { this->SetNodeClean(); } + + mork_bool IsStoreClean() const { return this->IsNodeClean(); } + mork_bool IsStoreDirty() const { return this->IsNodeDirty(); } + +public: // setting dirty based on CanDirty: + + void MaybeDirtyStore() + { if ( mStore_CanDirty ) this->SetStoreDirty(); } + +public: // space waste analysis + + mork_percent PercentOfStoreWasted(morkEnv* ev); + +public: // setting store and all subspaces canDirty: + + void SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty); + +public: // building an atom inside mStore_FarBookAtom from a char* string + + morkFarBookAtom* StageAliasAsFarBookAtom(morkEnv* ev, + const morkMid* inMid, morkAtomSpace* ioSpace, mork_cscode inForm); + + morkFarBookAtom* StageYarnAsFarBookAtom(morkEnv* ev, + const mdbYarn* inYarn, morkAtomSpace* ioSpace); + + morkFarBookAtom* StageStringAsFarBookAtom(morkEnv* ev, + const char* inString, mork_cscode inForm, morkAtomSpace* ioSpace); + +public: // determining whether incremental writing is a good use of time: + + mork_bool DoPreferLargeOverCompressCommit(morkEnv* ev); + // true when mStore_CanWriteIncremental && store has file large enough + +public: // lazy creation of members and nested row or atom spaces + + morkAtomSpace* LazyGetOidAtomSpace(morkEnv* ev); + morkAtomSpace* LazyGetGroundAtomSpace(morkEnv* ev); + morkAtomSpace* LazyGetGroundColumnSpace(morkEnv* ev); + + morkStream* LazyGetInStream(morkEnv* ev); + morkBuilder* LazyGetBuilder(morkEnv* ev); + void ForgetBuilder(morkEnv* ev); + + morkStream* LazyGetOutStream(morkEnv* ev); + + morkRowSpace* LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope); + morkAtomSpace* LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope); + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseStore() only if open + +public: // morkStore construction & destruction + morkStore(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance + morkFactory* inFactory, // the factory for this + nsIMdbHeap* ioPortHeap // the heap to hold all content in the port + ); + void CloseStore(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkStore(const morkStore& other); + morkStore& operator=(const morkStore& other); + virtual ~morkStore(); // assert that CloseStore() executed earlier + +public: // dynamic type identification + morkEnv* CanUseStore(nsIMdbEnv* mev, mork_bool inMutable, + nsresult* outErr) const; + mork_bool IsStore() const + { return IsNode() && mNode_Derived == morkDerived_kStore; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonStoreTypeError(morkEnv* ev); + static void NilStoreFileError(morkEnv* ev); + static void CannotAutoAssignAtomIdentityError(morkEnv* ev); + +public: // store utilties + + morkAtom* YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, bool createIfMissing = true); + morkAtom* AddAlias(morkEnv* ev, const morkMid& inMid, + mork_cscode inForm); + +public: // other store methods + + void RenumberAllCollectableContent(morkEnv* ev); + + nsIMdbStore* AcquireStoreHandle(morkEnv* ev); // mObject_Handle + + morkPool* StorePool() { return &mStore_Pool; } + + mork_bool OpenStoreFile(morkEnv* ev, // return value equals ev->Good() + mork_bool inFrozen, + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy); + + mork_bool CreateStoreFile(morkEnv* ev, // return value equals ev->Good() + // const char* inFilePath, + nsIMdbFile* ioFile, // db abstract file interface + const mdbOpenPolicy* inOpenPolicy); + + morkAtom* CopyAtom(morkEnv* ev, const morkAtom* inAtom); + // copy inAtom (from some other store) over to this store + + mork_token CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore); + // copy inToken from inStore over to this store + + mork_token BufToToken(morkEnv* ev, const morkBuf* inBuf); + mork_token StringToToken(morkEnv* ev, const char* inTokenName); + mork_token QueryToken(morkEnv* ev, const char* inTokenName); + void TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName); + + mork_bool MidToOid(morkEnv* ev, const morkMid& inMid, + mdbOid* outOid); + mork_bool OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn); + mork_bool MidToYarn(morkEnv* ev, const morkMid& inMid, + mdbYarn* outYarn); + + morkBookAtom* MidToAtom(morkEnv* ev, const morkMid& inMid); + morkRow* MidToRow(morkEnv* ev, const morkMid& inMid); + morkTable* MidToTable(morkEnv* ev, const morkMid& inMid); + + morkRow* OidToRow(morkEnv* ev, const mdbOid* inOid); + // OidToRow() finds old row with oid, or makes new one if not found. + + morkTable* OidToTable(morkEnv* ev, const mdbOid* inOid, + const mdbOid* inOptionalMetaRowOid); + // OidToTable() finds old table with oid, or makes new one if not found. + + static void SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken, + mdbYarn* outYarn); + + mork_bool HasTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_count* outTableCount); + + morkTable* GetTableKind(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_count* outTableCount, + mdb_bool* outMustBeUnique); + + morkRow* FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn, + const mdbYarn* inTargetCellValue); + + morkRow* GetRow(morkEnv* ev, const mdbOid* inOid); + morkTable* GetTable(morkEnv* ev, const mdbOid* inOid); + + morkTable* NewTable(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind, mdb_bool inMustBeUnique, + const mdbOid* inOptionalMetaRowOid); + + morkPortTableCursor* GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope, + mdb_kind inTableKind) ; + + morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid); + morkRow* NewRow(morkEnv* ev, mdb_scope inRowScope); + + morkThumb* MakeCompressCommitThumb(morkEnv* ev, mork_bool inDoCollect); + +public: // commit related methods + + mork_bool MarkAllStoreContentDirty(morkEnv* ev); + // MarkAllStoreContentDirty() visits every object in the store and marks + // them dirty, including every table, row, cell, and atom. The return + // equals ev->Good(), to show whether any error happened. This method is + // intended for use in the beginning of a "compress commit" which writes + // all store content, whether dirty or not. We dirty everything first so + // that later iterations over content can mark things clean as they are + // written, and organize the process of serialization so that objects are + // written only at need (because of being dirty). + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakStore(morkStore* me, + morkEnv* ev, morkStore** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongStore(morkStore* me, + morkEnv* ev, morkStore** ioSlot) + { + morkStore* store = *ioSlot; + if ( me != store ) + { + if ( store ) + { + // what if this nulls out the ev and causes asserts? + // can we move this after the CutStrongRef()? + *ioSlot = 0; + store->Release(); + } + if ( me && me->AddRef() ) + *ioSlot = me; + } + } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSTORE_ */ + diff --git a/db/mork/src/morkStream.cpp b/db/mork/src/morkStream.cpp new file mode 100644 index 0000000000..26c6f443e5 --- /dev/null +++ b/db/mork/src/morkStream.cpp @@ -0,0 +1,859 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKFILE_ +#include "morkFile.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKSTREAM_ +#include "morkStream.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkStream::CloseMorkNode(morkEnv* ev) // CloseStream() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseStream(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkStream::~morkStream() // assert CloseStream() executed earlier +{ + MORK_ASSERT(mStream_ContentFile==0); + MORK_ASSERT(mStream_Buf==0); +} + +/*public non-poly*/ +morkStream::morkStream(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, + nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen) +: morkFile(ev, inUsage, ioHeap, ioHeap) +, mStream_At( 0 ) +, mStream_ReadEnd( 0 ) +, mStream_WriteEnd( 0 ) + +, mStream_ContentFile( 0 ) + +, mStream_Buf( 0 ) +, mStream_BufSize( inBufSize ) +, mStream_BufPos( 0 ) +, mStream_Dirty( morkBool_kFalse ) +, mStream_HitEof( morkBool_kFalse ) +{ + if ( ev->Good() ) + { + if ( inBufSize < morkStream_kMinBufSize ) + mStream_BufSize = inBufSize = morkStream_kMinBufSize; + else if ( inBufSize > morkStream_kMaxBufSize ) + mStream_BufSize = inBufSize = morkStream_kMaxBufSize; + + if ( ioContentFile && ioHeap ) + { + // if ( ioContentFile->FileFrozen() ) // forced to be readonly? + // inFrozen = morkBool_kTrue; // override the input value + + nsIMdbFile_SlotStrongFile(ioContentFile, ev, &mStream_ContentFile); + if ( ev->Good() ) + { + mork_u1* buf = 0; + ioHeap->Alloc(ev->AsMdbEnv(), inBufSize, (void**) &buf); + if ( buf ) + { + mStream_At = mStream_Buf = buf; + + if ( !inFrozen ) + { + // physical buffer end never moves: + mStream_WriteEnd = buf + inBufSize; + } + else + mStream_WriteEnd = 0; // no writing is allowed + + if ( inFrozen ) + { + // logical buffer end starts at Buf with no content: + mStream_ReadEnd = buf; + this->SetFileFrozen(inFrozen); + } + else + mStream_ReadEnd = 0; // no reading is allowed + + this->SetFileActive(morkBool_kTrue); + this->SetFileIoOpen(morkBool_kTrue); + } + if ( ev->Good() ) + mNode_Derived = morkDerived_kStream; + } + } + else ev->NilPointerError(); + } +} + +/*public non-poly*/ void +morkStream::CloseStream(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mStream_ContentFile); + nsIMdbHeap* heap = mFile_SlotHeap; + mork_u1* buf = mStream_Buf; + mStream_Buf = 0; + + if ( heap && buf ) + heap->Free(ev->AsMdbEnv(), buf); + + this->CloseFile(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +#define morkStream_kSpacesPerIndent 1 /* one space per indent */ +#define morkStream_kMaxIndentDepth 70 /* max indent of 70 space bytes */ +static const char morkStream_kSpaces[] // next line to ease length perception += " "; +// 123456789_123456789_123456789_123456789_123456789_123456789_123456789_ +// morkStream_kSpaces above must contain (at least) 70 spaces (ASCII 0x20) + +mork_size +morkStream::PutIndent(morkEnv* ev, mork_count inDepth) + // PutIndent() puts a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + nsIMdbEnv *mev = ev->AsMdbEnv(); + if ( ev->Good() ) + { + this->PutLineBreak(ev); + if ( ev->Good() ) + { + outLength = inDepth; + mdb_size bytesWritten; + if ( inDepth ) + this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size +morkStream::PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth) + // PutByteThenIndent() puts the byte, then a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + nsIMdbEnv *mev = ev->AsMdbEnv(); + + if ( inDepth > morkStream_kMaxIndentDepth ) + inDepth = morkStream_kMaxIndentDepth; + + this->Putc(ev, inByte); + if ( ev->Good() ) + { + this->PutLineBreak(ev); + if ( ev->Good() ) + { + outLength = inDepth; + mdb_size bytesWritten; + if ( inDepth ) + this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size +morkStream::PutStringThenIndent(morkEnv* ev, + const char* inString, mork_count inDepth) +// PutStringThenIndent() puts the string, then a linebreak, and then +// "indents" by inDepth, and returns the line length after indentation. +{ + mork_size outLength = 0; + mdb_size bytesWritten; + nsIMdbEnv *mev = ev->AsMdbEnv(); + + if ( inDepth > morkStream_kMaxIndentDepth ) + inDepth = morkStream_kMaxIndentDepth; + + if ( inString ) + { + mork_size length = MORK_STRLEN(inString); + if ( length && ev->Good() ) // any bytes to write? + this->Write(mev, inString, length, &bytesWritten); + } + + if ( ev->Good() ) + { + this->PutLineBreak(ev); + if ( ev->Good() ) + { + outLength = inDepth; + if ( inDepth ) + this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten); + } + } + return outLength; +} + +mork_size +morkStream::PutString(morkEnv* ev, const char* inString) +{ + nsIMdbEnv *mev = ev->AsMdbEnv(); + mork_size outSize = 0; + mdb_size bytesWritten; + if ( inString ) + { + outSize = MORK_STRLEN(inString); + if ( outSize && ev->Good() ) // any bytes to write? + { + this->Write(mev, inString, outSize, &bytesWritten); + } + } + return outSize; +} + +mork_size +morkStream::PutStringThenNewline(morkEnv* ev, const char* inString) + // PutStringThenNewline() returns total number of bytes written. +{ + nsIMdbEnv *mev = ev->AsMdbEnv(); + mork_size outSize = 0; + mdb_size bytesWritten; + if ( inString ) + { + outSize = MORK_STRLEN(inString); + if ( outSize && ev->Good() ) // any bytes to write? + { + this->Write(mev, inString, outSize, &bytesWritten); + if ( ev->Good() ) + outSize += this->PutLineBreak(ev); + } + } + return outSize; +} + +mork_size +morkStream::PutByteThenNewline(morkEnv* ev, int inByte) + // PutByteThenNewline() returns total number of bytes written. +{ + mork_size outSize = 1; // one for the following byte + this->Putc(ev, inByte); + if ( ev->Good() ) + outSize += this->PutLineBreak(ev); + return outSize; +} + +mork_size +morkStream::PutLineBreak(morkEnv* ev) +{ +#if defined(MORK_MAC) + + this->Putc(ev, mork_kCR); + return 1; + +#else +# if defined(MORK_WIN) + + this->Putc(ev, mork_kCR); + this->Putc(ev, mork_kLF); + return 2; + +# else +# ifdef MORK_UNIX + + this->Putc(ev, mork_kLF); + return 1; + +# endif /* MORK_UNIX */ +# endif /* MORK_WIN */ +#endif /* MORK_MAC */ +} +// ````` ````` ````` ````` ````` ````` ````` ````` +// public: // virtual morkFile methods + + +NS_IMETHODIMP +morkStream::Steal(nsIMdbEnv* mev, nsIMdbFile* ioThief) + // Steal: tell this file to close any associated i/o stream in the file + // system, because the file ioThief intends to reopen the file in order + // to provide the MDB implementation with more exotic file access than is + // offered by the nsIMdbFile alone. Presumably the thief knows enough + // from Path() in order to know which file to reopen. If Steal() is + // successful, this file should probably delegate all future calls to + // the nsIMdbFile interface down to the thief files, so that even after + // the file has been stolen, it can still be read, written, or forcibly + // closed (by a call to CloseMdbObject()). +{ + MORK_USED_1(ioThief); + morkEnv *ev = morkEnv::FromMdbEnv(mev); + ev->StubMethodOnlyError(); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStream::BecomeTrunk(nsIMdbEnv* mev) + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. +{ + morkEnv *ev = morkEnv::FromMdbEnv(mev); + ev->StubMethodOnlyError(); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkStream::AcquireBud(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbFile **acqBud) + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. +{ + MORK_USED_1(ioHeap); + morkFile* outFile = 0; + nsIMdbFile* file = mStream_ContentFile; + morkEnv *ev = morkEnv::FromMdbEnv(mev); + if ( this->IsOpenAndActiveFile() && file ) + { + // figure out how this interacts with buffering and mStream_WriteEnd: + ev->StubMethodOnlyError(); + } + else this->NewFileDownError(ev); + + *acqBud = outFile; + return NS_ERROR_NOT_IMPLEMENTED; +} + +mork_pos +morkStream::Length(morkEnv* ev) const // eof +{ + mork_pos outPos = 0; + + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenAndActiveFile() && file ) + { + mork_pos contentEof = 0; + file->Eof(ev->AsMdbEnv(), &contentEof); + if ( ev->Good() ) + { + if ( mStream_WriteEnd ) // this stream supports writing? + { + // the local buffer might have buffered content past content eof + if ( ev->Good() ) // no error happened during Length() above? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if ( at >= buf ) // expected cursor order? + { + mork_pos localContent = mStream_BufPos + (at - buf); + if ( localContent > contentEof ) // buffered past eof? + contentEof = localContent; // return new logical eof + + outPos = contentEof; + } + else this->NewBadCursorOrderError(ev); + } + } + else + outPos = contentEof; // frozen files get length from content file + } + } + else this->NewFileDownError(ev); + + return outPos; +} + +void morkStream::NewBadCursorSlotsError(morkEnv* ev) const +{ ev->NewError("bad stream cursor slots"); } + +void morkStream::NewNullStreamBufferError(morkEnv* ev) const +{ ev->NewError("null stream buffer"); } + +void morkStream::NewCantReadSinkError(morkEnv* ev) const +{ ev->NewError("cant read stream sink"); } + +void morkStream::NewCantWriteSourceError(morkEnv* ev) const +{ ev->NewError("cant write stream source"); } + +void morkStream::NewPosBeyondEofError(morkEnv* ev) const +{ ev->NewError("stream pos beyond eof"); } + +void morkStream::NewBadCursorOrderError(morkEnv* ev) const +{ ev->NewError("bad stream cursor order"); } + +NS_IMETHODIMP +morkStream::Tell(nsIMdbEnv* mdbev, mork_pos *aOutPos) const +{ + nsresult rv = NS_OK; + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + + NS_ENSURE_ARG_POINTER(aOutPos); + + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenAndActiveFile() && file ) + { + mork_u1* buf = mStream_Buf; + mork_u1* at = mStream_At; + + mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly + mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly + + if ( writeEnd ) + { + if ( buf && at >= buf && at <= writeEnd ) + { + *aOutPos = mStream_BufPos + (at - buf); + } + else this->NewBadCursorOrderError(ev); + } + else if ( readEnd ) + { + if ( buf && at >= buf && at <= readEnd ) + { + *aOutPos = mStream_BufPos + (at - buf); + } + else this->NewBadCursorOrderError(ev); + } + } + else this->NewFileDownError(ev); + + return rv; +} + +NS_IMETHODIMP +morkStream::Read(nsIMdbEnv* mdbev, void* outBuf, mork_size inSize, mork_size *aOutSize) +{ + NS_ENSURE_ARG_POINTER(aOutSize); + // First we satisfy the request from buffered bytes, if any. Then + // if additional bytes are needed, we satisfy these by direct reads + // from the content file without any local buffering (but we still need + // to adjust the buffer position to reflect the current i/o point). + + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + nsresult rv = NS_OK; + + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenAndActiveFile() && file ) + { + mork_u1* end = mStream_ReadEnd; // byte after last buffered byte + if ( end ) // file is open for read access? + { + if ( inSize ) // caller wants any output? + { + mork_u1* sink = (mork_u1*) outBuf; // where we plan to write bytes + if ( sink ) // caller passed good buffer address? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if ( at >= buf && at <= end ) // expected cursor order? + { + mork_num remaining = (mork_num) (end - at); // bytes left in buffer + + mork_num quantum = inSize; // number of bytes to copy + if ( quantum > remaining ) // more than buffer content? + quantum = remaining; // restrict to buffered bytes + + if ( quantum ) // any bytes left in the buffer? + { + MORK_MEMCPY(sink, at, quantum); // from buffer bytes + + at += quantum; // advance past read bytes + mStream_At = at; + *aOutSize += quantum; // this much copied so far + + sink += quantum; // in case we need to copy more + inSize -= quantum; // filled this much of request + mStream_HitEof = morkBool_kFalse; + } + + if ( inSize ) // we still need to read more content? + { + // We need to read more bytes directly from the + // content file, without local buffering. We have + // exhausted the local buffer, so we need to show + // it is now empty, and adjust the current buf pos. + + mork_num posDelta = (mork_num) (at - buf); // old buf content + mStream_BufPos += posDelta; // past now empty buf + + mStream_At = mStream_ReadEnd = buf; // empty buffer + + // file->Seek(ev, mStream_BufPos); // set file pos + // if ( ev->Good() ) // no seek error? + // { + // } + + mork_num actual = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Get(menv, sink, inSize, mStream_BufPos, &actual); + if ( ev->Good() ) // no read error? + { + if ( actual ) + { + *aOutSize += actual; + mStream_BufPos += actual; + mStream_HitEof = morkBool_kFalse; + } + else if ( !*aOutSize ) + mStream_HitEof = morkBool_kTrue; + } + } + } + else this->NewBadCursorOrderError(ev); + } + else this->NewNullStreamBufferError(ev); + } + } + else this->NewCantReadSinkError(ev); + } + else this->NewFileDownError(ev); + + if ( ev->Bad() ) + *aOutSize = 0; + + return rv; +} + +NS_IMETHODIMP +morkStream::Seek(nsIMdbEnv * mdbev, mork_pos inPos, mork_pos *aOutPos) +{ + NS_ENSURE_ARG_POINTER(aOutPos); + morkEnv *ev = morkEnv::FromMdbEnv(mdbev); + *aOutPos = 0; + nsresult rv = NS_OK; + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenOrClosingNode() && this->FileActive() && file ) + { + mork_u1* at = mStream_At; // current position in buffer + mork_u1* buf = mStream_Buf; // beginning of buffer + mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly + mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly + + if ( writeEnd ) // file is mutable/writeonly? + { + if ( mStream_Dirty ) // need to commit buffer changes? + this->Flush(mdbev); + + if ( ev->Good() ) // no errors during flush or earlier? + { + if ( at == buf ) // expected post flush cursor value? + { + if ( mStream_BufPos != inPos ) // need to change pos? + { + mork_pos eof = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Eof(menv, &eof); + if ( ev->Good() ) // no errors getting length? + { + if ( inPos <= eof ) // acceptable new position? + { + mStream_BufPos = inPos; // new stream position + *aOutPos = inPos; + } + else this->NewPosBeyondEofError(ev); + } + } + } + else this->NewBadCursorOrderError(ev); + } + } + else if ( readEnd ) // file is frozen/readonly? + { + if ( at >= buf && at <= readEnd ) // expected cursor order? + { + mork_pos eof = 0; + nsIMdbEnv* menv = ev->AsMdbEnv(); + file->Eof(menv, &eof); + if ( ev->Good() ) // no errors getting length? + { + if ( inPos <= eof ) // acceptable new position? + { + *aOutPos = inPos; + mStream_BufPos = inPos; // new stream position + mStream_At = mStream_ReadEnd = buf; // empty buffer + if ( inPos == eof ) // notice eof reached? + mStream_HitEof = morkBool_kTrue; + } + else this->NewPosBeyondEofError(ev); + } + } + else this->NewBadCursorOrderError(ev); + } + + } + else this->NewFileDownError(ev); + + return rv; +} + +NS_IMETHODIMP +morkStream::Write(nsIMdbEnv* menv, const void* inBuf, mork_size inSize, mork_size *aOutSize) +{ + mork_num outActual = 0; + morkEnv *ev = morkEnv::FromMdbEnv(menv); + + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenActiveAndMutableFile() && file ) + { + mork_u1* end = mStream_WriteEnd; // byte after last buffered byte + if ( end ) // file is open for write access? + { + if ( inSize ) // caller provided any input? + { + const mork_u1* source = (const mork_u1*) inBuf; // from where + if ( source ) // caller passed good buffer address? + { + mork_u1* at = mStream_At; + mork_u1* buf = mStream_Buf; + if ( at >= buf && at <= end ) // expected cursor order? + { + mork_num space = (mork_num) (end - at); // space left in buffer + + mork_num quantum = inSize; // number of bytes to write + if ( quantum > space ) // more than buffer size? + quantum = space; // restrict to avail space + + if ( quantum ) // any space left in the buffer? + { + mStream_Dirty = morkBool_kTrue; // to ensure later flush + MORK_MEMCPY(at, source, quantum); // into buffer + + mStream_At += quantum; // advance past written bytes + outActual += quantum; // this much written so far + + source += quantum; // in case we need to write more + inSize -= quantum; // filled this much of request + } + + if ( inSize ) // we still need to write more content? + { + // We need to write more bytes directly to the + // content file, without local buffering. We have + // exhausted the local buffer, so we need to flush + // it and empty it, and adjust the current buf pos. + // After flushing, if the rest of the write fits + // inside the buffer, we will put bytes into the + // buffer rather than write them to content file. + + if ( mStream_Dirty ) + this->Flush(menv); // will update mStream_BufPos + + at = mStream_At; + if ( at < buf || at > end ) // bad cursor? + this->NewBadCursorOrderError(ev); + + if ( ev->Good() ) // no errors? + { + space = (mork_num) (end - at); // space left in buffer + if ( space > inSize ) // write to buffer? + { + mStream_Dirty = morkBool_kTrue; // ensure flush + MORK_MEMCPY(at, source, inSize); // copy + + mStream_At += inSize; // past written bytes + outActual += inSize; // this much written + } + else // directly to content file instead + { + // file->Seek(ev, mStream_BufPos); // set pos + // if ( ev->Good() ) // no seek error? + // { + // } + + mork_num actual = 0; + file->Put(menv, source, inSize, mStream_BufPos, &actual); + if ( ev->Good() ) // no write error? + { + outActual += actual; + mStream_BufPos += actual; + } + } + } + } + } + else this->NewBadCursorOrderError(ev); + } + else this->NewNullStreamBufferError(ev); + } + } + else this->NewCantWriteSourceError(ev); + } + else this->NewFileDownError(ev); + + if ( ev->Bad() ) + outActual = 0; + + *aOutSize = outActual; + return ev->AsErr(); +} + +NS_IMETHODIMP +morkStream::Flush(nsIMdbEnv* ev) +{ + morkEnv *mev = morkEnv::FromMdbEnv(ev); + nsresult rv = NS_ERROR_FAILURE; + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenOrClosingNode() && this->FileActive() && file ) + { + if ( mStream_Dirty ) + this->spill_buf(mev); + + rv = file->Flush(ev); + } + else this->NewFileDownError(mev); + return rv; +} + +// ````` ````` ````` ````` ````` ````` ````` ````` +// protected: // protected non-poly morkStream methods (for char io) + +int +morkStream::fill_getc(morkEnv* ev) +{ + int c = EOF; + + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenAndActiveFile() && file ) + { + mork_u1* buf = mStream_Buf; + mork_u1* end = mStream_ReadEnd; // beyond buf after earlier read + if ( end > buf ) // any earlier read bytes buffered? + { + mStream_BufPos += ( end - buf ); // advance past old read + } + + if ( ev->Good() ) // no errors yet? + { + // file->Seek(ev, mStream_BufPos); // set file pos + // if ( ev->Good() ) // no seek error? + // { + // } + + nsIMdbEnv* menv = ev->AsMdbEnv(); + mork_num actual = 0; + file->Get(menv, buf, mStream_BufSize, mStream_BufPos, &actual); + if ( ev->Good() ) // no read errors? + { + if ( actual > mStream_BufSize ) // more than asked for?? + actual = mStream_BufSize; + + mStream_At = buf; + mStream_ReadEnd = buf + actual; + if ( actual ) // any bytes actually read? + { + c = *mStream_At++; // return first byte from buffer + mStream_HitEof = morkBool_kFalse; + } + else + mStream_HitEof = morkBool_kTrue; + } + } + } + else this->NewFileDownError(ev); + + return c; +} + +void +morkStream::spill_putc(morkEnv* ev, int c) +{ + this->spill_buf(ev); + if ( ev->Good() && mStream_At < mStream_WriteEnd ) + this->Putc(ev, c); +} + +void +morkStream::spill_buf(morkEnv* ev) // spill/flush from buffer to file +{ + nsIMdbFile* file = mStream_ContentFile; + if ( this->IsOpenOrClosingNode() && this->FileActive() && file ) + { + mork_u1* buf = mStream_Buf; + if ( mStream_Dirty ) + { + mork_u1* at = mStream_At; + if ( at >= buf && at <= mStream_WriteEnd ) // order? + { + mork_num count = (mork_num) (at - buf); // bytes buffered + if ( count ) // anything to write to the string? + { + if ( count > mStream_BufSize ) // no more than max? + { + count = mStream_BufSize; + mStream_WriteEnd = buf + mStream_BufSize; + this->NewBadCursorSlotsError(ev); + } + if ( ev->Good() ) + { + // file->Seek(ev, mStream_BufPos); + // if ( ev->Good() ) + // { + // } + nsIMdbEnv* menv = ev->AsMdbEnv(); + mork_num actual = 0; + + file->Put(menv, buf, count, mStream_BufPos, &actual); + if ( ev->Good() ) + { + mStream_BufPos += actual; // past bytes written + mStream_At = buf; // reset buffer cursor + mStream_Dirty = morkBool_kFalse; + } + } + } + } + else this->NewBadCursorOrderError(ev); + } + else + { +#ifdef MORK_DEBUG + ev->NewWarning("stream:spill:not:dirty"); +#endif /*MORK_DEBUG*/ + } + } + else this->NewFileDownError(ev); + +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkStream.h b/db/mork/src/morkStream.h new file mode 100644 index 0000000000..418b68070e --- /dev/null +++ b/db/mork/src/morkStream.h @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MORKSTREAM_ +#define _MORKSTREAM_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKFILE_ +#include "morkFile.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*============================================================================= + * morkStream: buffered file i/o + */ + +/*| morkStream exists to define an morkFile subclass that provides buffered +**| i/o for an underlying content file. Naturally this arrangement only makes +**| sense when the underlying content file is itself not efficiently buffered +**| (especially for character by character i/o). +**| +**|| morkStream is intended for either reading use or writing use, but not +**| both simultaneously or interleaved. Pick one when the stream is created +**| and don't change your mind. This restriction is intended to avoid obscure +**| and complex bugs that might arise from interleaved reads and writes -- so +**| just don't do it. A stream is either a sink or a source, but not both. +**| +**|| (When the underlying content file is intended to support both reading and +**| writing, a developer might use two instances of morkStream where one is for +**| reading and the other is for writing. In this case, a developer must take +**| care to keep the two streams in sync because each will maintain a separate +**| buffer representing a cache consistency problem for the other. A simple +**| approach is to invalidate the buffer of one when one uses the other, with +**| the assumption that closely mixed reading and writing is not expected, so +**| that little cost is associated with changing read/write streaming modes.) +**| +**|| Exactly one of mStream_ReadEnd or mStream_WriteEnd must be a null pointer, +**| and this will cause the right thing to occur when inlines use them, because +**| mStream_At < mStream_WriteEnd (for example) will always be false and the +**| else branch of the statement calls a function that raises an appropriate +**| error to complain about either reading a sink or writing a source. +**| +**|| morkStream is a direct clone of ab_Stream from Communicator 4.5's +**| address book code, which in turn was based on the stream class in the +**| public domain Mithril programming language. +|*/ + +#define morkStream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */ + +#define morkStream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */ +#define morkStream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */ + +#define morkDerived_kStream /*i*/ 0x7A74 /* ascii 'zt' */ + +class morkStream /*d*/ : public morkFile { /* from Mithril's AgStream class */ + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected morkStream members + mork_u1* mStream_At; // pointer into mStream_Buf + mork_u1* mStream_ReadEnd; // null or one byte past last readable byte + mork_u1* mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize + + nsIMdbFile* mStream_ContentFile; // where content is read and written + + mork_u1* mStream_Buf; // dynamically allocated memory to buffer io + mork_size mStream_BufSize; // requested buf size (fixed by min and max) + mork_pos mStream_BufPos; // logical position of byte at mStream_Buf + mork_bool mStream_Dirty; // does the buffer need to be flushed? + mork_bool mStream_HitEof; // has eof been reached? (only frozen streams) + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseStream() only if open + virtual ~morkStream(); // assert that CloseStream() executed earlier + +public: // morkStream construction & destruction + morkStream(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen); + void CloseStream(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkStream(const morkStream& other); + morkStream& operator=(const morkStream& other); + +public: // dynamic type identification + mork_bool IsStream() const + { return IsNode() && mNode_Derived == morkDerived_kStream; } +// } ===== end morkNode methods ===== + +public: // typing + void NonStreamTypeError(morkEnv* ev); + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // virtual morkFile methods + + NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override; + // Steal: tell this file to close any associated i/o stream in the file + // system, because the file ioThief intends to reopen the file in order + // to provide the MDB implementation with more exotic file access than is + // offered by the nsIMdbFile alone. Presumably the thief knows enough + // from Path() in order to know which file to reopen. If Steal() is + // successful, this file should probably delegate all future calls to + // the nsIMdbFile interface down to the thief files, so that even after + // the file has been stolen, it can still be read, written, or forcibly + // closed (by a call to CloseMdbObject()). + + NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override; + // If this file is a file version branch created by calling AcquireBud(), + // BecomeTrunk() causes this file's content to replace the original + // file's content, typically by assuming the original file's identity. + + NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbFile** acqBud) override; + // AcquireBud() starts a new "branch" version of the file, empty of content, + // so that a new version of the file can be written. This new file + // can later be told to BecomeTrunk() the original file, so the branch + // created by budding the file will replace the original file. Some + // file subclasses might initially take the unsafe but expedient + // approach of simply truncating this file down to zero length, and + // then returning the same morkFile pointer as this, with an extra + // reference count increment. Note that the caller of AcquireBud() is + // expected to eventually call CutStrongRef() on the returned file + // in order to release the strong reference. High quality versions + // of morkFile subclasses will create entirely new files which later + // are renamed to become the old file, so that better transactional + // behavior is exhibited by the file, so crashes protect old files. + // Note that AcquireBud() is an illegal operation on readonly files. + + virtual mork_pos Length(morkEnv* ev) const override; // eof + NS_IMETHOD Tell(nsIMdbEnv* ev, mork_pos *aOutPos ) const override; + NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_size *aOutCount) override; + NS_IMETHOD Seek(nsIMdbEnv* ev, mork_pos inPos, mork_pos *aOutPos) override; + NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mork_size inSize, mork_size *aOutCount) override; + NS_IMETHOD Flush(nsIMdbEnv* ev) override; + +// ````` ````` ````` ````` ````` ````` ````` ````` +protected: // protected non-poly morkStream methods (for char io) + + int fill_getc(morkEnv* ev); + void spill_putc(morkEnv* ev, int c); + void spill_buf(morkEnv* ev); // spill/flush from buffer to file + +// ````` ````` ````` ````` ````` ````` ````` ````` +public: // public non-poly morkStream methods + + void NewBadCursorSlotsError(morkEnv* ev) const; + void NewBadCursorOrderError(morkEnv* ev) const; + void NewNullStreamBufferError(morkEnv* ev) const; + void NewCantReadSinkError(morkEnv* ev) const; + void NewCantWriteSourceError(morkEnv* ev) const; + void NewPosBeyondEofError(morkEnv* ev) const; + + nsIMdbFile* GetStreamContentFile() const { return mStream_ContentFile; } + mork_size GetStreamBufferSize() const { return mStream_BufSize; } + + mork_size PutIndent(morkEnv* ev, mork_count inDepth); + // PutIndent() puts a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth); + // PutByteThenIndent() puts the byte, then a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutStringThenIndent(morkEnv* ev, + const char* inString, mork_count inDepth); + // PutStringThenIndent() puts the string, then a linebreak, and then + // "indents" by inDepth, and returns the line length after indentation. + + mork_size PutString(morkEnv* ev, const char* inString); + // PutString() returns the length of the string written. + + mork_size PutStringThenNewline(morkEnv* ev, const char* inString); + // PutStringThenNewline() returns total number of bytes written. + + mork_size PutByteThenNewline(morkEnv* ev, int inByte); + // PutByteThenNewline() returns total number of bytes written. + + // ````` ````` stdio type methods ````` ````` + void Ungetc(int c) /*i*/ + { if ( mStream_At > mStream_Buf && c > 0 ) *--mStream_At = (mork_u1) c; } + + // Note Getc() returns EOF consistently after any fill_getc() error occurs. + int Getc(morkEnv* ev) /*i*/ + { return ( mStream_At < mStream_ReadEnd )? *mStream_At++ : fill_getc(ev); } + + void Putc(morkEnv* ev, int c) /*i*/ + { + mStream_Dirty = morkBool_kTrue; + if ( mStream_At < mStream_WriteEnd ) + *mStream_At++ = (mork_u1) c; + else + spill_putc(ev, c); + } + + mork_size PutLineBreak(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakStream(morkStream* me, + morkEnv* ev, morkStream** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongStream(morkStream* me, + morkEnv* ev, morkStream** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKSTREAM_ */ diff --git a/db/mork/src/morkTable.cpp b/db/mork/src/morkTable.cpp new file mode 100644 index 0000000000..aaa4bd50e4 --- /dev/null +++ b/db/mork/src/morkTable.cpp @@ -0,0 +1,1610 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKARRAY_ +#include "morkArray.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKTABLEROWCURSOR_ +#include "morkTableRowCursor.h" +#endif + +#ifndef _MORKROWOBJECT_ +#include "morkRowObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkTable::CloseMorkNode(morkEnv* ev) /*i*/ // CloseTable() only if open +{ + if ( this->IsOpenNode() ) + { + morkObject::CloseMorkNode(ev); // give base class a chance. + this->MarkClosing(); + this->CloseTable(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mTable_Store==0); + MORK_ASSERT(mTable_RowSpace==0); +} + +/*public non-poly*/ +morkTable::morkTable(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace, + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique) +: morkObject(ev, inUsage, ioHeap, (mork_color) inTid, (morkHandle*) 0) +, mTable_Store( 0 ) +, mTable_RowSpace( 0 ) +, mTable_MetaRow( 0 ) + +, mTable_RowMap( 0 ) +// , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap, +// morkTable_kStartRowMapSlotCount) +, mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*) 0, + morkTable_kStartRowArraySize, ioSlotHeap) + +, mTable_ChangeList() +, mTable_ChangesCount( 0 ) +, mTable_ChangesMax( 3 ) // any very small number greater than zero + +, mTable_Kind( inKind ) + +, mTable_Flags( 0 ) +, mTable_Priority( morkPriority_kLo ) // NOT high priority +, mTable_GcUses( 0 ) +, mTable_Pad( 0 ) +{ + this->mLink_Next = 0; + this->mLink_Prev = 0; + + if ( ev->Good() ) + { + if ( ioStore && ioSlotHeap && ioRowSpace ) + { + if ( inKind ) + { + if ( inMustBeUnique ) + this->SetTableUnique(); + mTable_Store = ioStore; + mTable_RowSpace = ioRowSpace; + if ( inOptionalMetaRowOid ) + mTable_MetaRowOid = *inOptionalMetaRowOid; + else + { + mTable_MetaRowOid.mOid_Scope = 0; + mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid; + } + if ( ev->Good() ) + { + if ( this->MaybeDirtySpaceStoreAndTable() ) + this->SetTableRewrite(); // everything is dirty + + mNode_Derived = morkDerived_kTable; + } + this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store + } + else + ioRowSpace->ZeroKindError(ev); + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkTable, morkObject, nsIMdbTable) + +/*public non-poly*/ void +morkTable::CloseTable(morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkRowMap::SlotStrongRowMap((morkRowMap*) 0, ev, &mTable_RowMap); + // mTable_RowMap.CloseMorkNode(ev); + mTable_RowArray.CloseMorkNode(ev); + mTable_Store = 0; + mTable_RowSpace = 0; + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin nsIMdbCollection methods ===== + +// { ----- begin attribute methods ----- +NS_IMETHODIMP +morkTable::GetSeed(nsIMdbEnv* mev, + mdb_seed* outSeed) // member change count +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + *outSeed = mTable_RowArray.mArray_Seed; + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::GetCount(nsIMdbEnv* mev, + mdb_count* outCount) // member count +{ + NS_ENSURE_ARG_POINTER(outCount); + *outCount = mTable_RowArray.mArray_Fill; + return NS_OK; +} + +NS_IMETHODIMP +morkTable::GetPort(nsIMdbEnv* mev, + nsIMdbPort** acqPort) // collection container +{ + (void) morkEnv::FromMdbEnv(mev); + NS_ENSURE_ARG_POINTER(acqPort); + *acqPort = mTable_Store; + return NS_OK; +} +// } ----- end attribute methods ----- + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkTable::GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* mev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) // acquire new cursor instance +{ + return this->GetTableRowCursor(mev, inMemberPos, + (nsIMdbTableRowCursor**) acqCursor); +} +// } ----- end cursor methods ----- + +// { ----- begin ID methods ----- +NS_IMETHODIMP +morkTable::GetOid(nsIMdbEnv* mev, + mdbOid* outOid) // read object identity +{ + morkEnv* ev = morkEnv::FromMdbEnv(mev); + GetTableOid(ev, outOid); + return NS_OK; +} + +NS_IMETHODIMP +morkTable::BecomeContent(nsIMdbEnv* mev, + const mdbOid* inOid) // exchange content +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; + // remember table->MaybeDirtySpaceStoreAndTable(); +} + +// } ----- end ID methods ----- + +// { ----- begin activity dropping methods ----- +NS_IMETHODIMP +morkTable::DropActivity( // tell collection usage no longer expected + nsIMdbEnv* mev) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// } ----- end activity dropping methods ----- + +// } ===== end nsIMdbCollection methods ===== + +// { ===== begin nsIMdbTable methods ===== + +// { ----- begin attribute methods ----- + +NS_IMETHODIMP +morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( inPrio > morkPriority_kMax ) + inPrio = morkPriority_kMax; + + mTable_Priority = inPrio; + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio) +{ + nsresult outErr = NS_OK; + mork_priority prio = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + prio = mTable_Priority; + if ( prio > morkPriority_kMax ) + { + prio = morkPriority_kMax; + mTable_Priority = prio; + } + outErr = ev->AsErr(); + } + if ( outPrio ) + *outPrio = prio; + return outErr; +} + + +NS_IMETHODIMP +morkTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose) +{ + NS_ENSURE_ARG_POINTER(outBeVerbose); + *outBeVerbose = IsTableVerbose(); + return NS_OK; +} + +NS_IMETHODIMP +morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( inBeVerbose ) + SetTableVerbose(); + else + ClearTableVerbose(); + + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique) +{ + NS_ENSURE_ARG_POINTER(outIsUnique); + *outIsUnique = IsTableUnique(); + return NS_OK; +} + +NS_IMETHODIMP +morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind) +{ + NS_ENSURE_ARG_POINTER(outTableKind); + *outTableKind = mTable_Kind; + return NS_OK; +} + +NS_IMETHODIMP +morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) +{ + nsresult outErr = NS_OK; + mdb_scope rowScope = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mTable_RowSpace ) + rowScope = mTable_RowSpace->SpaceScope(); + else + NilRowSpaceError(ev); + + outErr = ev->AsErr(); + } + if ( outRowScope ) + *outRowScope = rowScope; + return outErr; +} + +NS_IMETHODIMP +morkTable::GetMetaRow( nsIMdbEnv* mev, + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mdbOid* outOid, // output meta row oid, can be nil to suppress output + nsIMdbRow** acqRow) // acquire table's unique singleton meta row + // The purpose of a meta row is to support the persistent recording of + // meta info about a table as cells put into the distinguished meta row. + // Each table has exactly one meta row, which is not considered a member + // of the collection of rows inside the table. The only way to tell + // whether a row is a meta row is by the fact that it is returned by this + // GetMetaRow() method from some table. Otherwise nothing distinguishes + // a meta row from any other row. A meta row can be used anyplace that + // any other row can be used, and can even be put into other tables (or + // the same table) as a table member, if this is useful for some reason. + // The first attempt to access a table's meta row using GetMetaRow() will + // cause the meta row to be created if it did not already exist. When the + // meta row is created, it will have the row oid that was previously + // requested for this table's meta row; or if no oid was ever explicitly + // specified for this meta row, then a unique oid will be generated in + // the row scope named "metaScope" (so obviously MDB clients should not + // manually allocate any row IDs from that special meta scope namespace). + // The meta row oid can be specified either when the table is created, or + // else the first time that GetMetaRow() is called, by passing a non-nil + // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's + // actual oid is returned in outOid (if this is a non-nil pointer), and + // it will be different from inOptionalMetaRowOid when the meta row was + // already given a different oid earlier. +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid); + if ( row && ev->Good() ) + { + if ( outOid ) + *outOid = row->mRow_Oid; + + outRow = row->AcquireRowHandle(ev, mTable_Store); + } + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + + if ( ev->Bad() && outOid ) + { + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkRow_kMinusOneRid; + } + return outErr; +} + +// } ----- end attribute methods ----- + +// { ----- begin cursor methods ----- +NS_IMETHODIMP +morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos + nsIMdbEnv* mev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance +{ + nsresult outErr = NS_OK; + nsIMdbTableRowCursor* outCursor = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos); + if ( cursor ) + { + if ( ev->Good() ) + { + // cursor->mCursor_Seed = (mork_seed) inRowPos; + outCursor = cursor; + outCursor->AddRef(); + } + } + + outErr = ev->AsErr(); + } + if ( acqCursor ) + *acqCursor = outCursor; + return outErr; +} +// } ----- end row position methods ----- + +// { ----- begin row position methods ----- +NS_IMETHODIMP +morkTable::PosToOid( // get row member for a table position + nsIMdbEnv* mev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) // row oid at the specified position +{ + nsresult outErr = NS_OK; + mdbOid roid; + roid.mOid_Scope = 0; + roid.mOid_Id = (mork_id) -1; + + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRow* row = SafeRowAt(ev, inRowPos); + if ( row ) + roid = row->mRow_Oid; + + outErr = ev->AsErr(); + } + if ( outOid ) + *outOid = roid; + return outErr; +} + +NS_IMETHODIMP +morkTable::OidToPos( // test for the table position of a row member + nsIMdbEnv* mev, // context + const mdbOid* inOid, // row to find in table + mdb_pos* outPos) // zero-based ordinal position of row in table +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mork_pos pos = ArrayHasOid(ev, inOid); + if ( outPos ) + *outPos = pos; + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::PosToRow( // get row member for a table position + nsIMdbEnv* mev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) // acquire row at table position inRowPos +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRow* row = SafeRowAt(ev, inRowPos); + if ( row && mTable_Store ) + outRow = row->AcquireRowHandle(ev, mTable_Store); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkTable::RowToPos( // test for the table position of a row member + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_pos* outPos) // zero-based ordinal position of row in table +{ + nsresult outErr = NS_OK; + mork_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject* row = (morkRowObject*) ioRow; + pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid); + outErr = ev->AsErr(); + } + if ( outPos ) + *outPos = pos; + return outErr; +} + +// Note that HasRow() performs the inverse oid->pos mapping +// } ----- end row position methods ----- + +// { ----- begin oid set methods ----- +NS_IMETHODIMP +morkTable::AddOid( // make sure the row with inOid is a table member + nsIMdbEnv* mev, // context + const mdbOid* inOid) // row to ensure membership in table +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::HasOid( // test for the table position of a row member + nsIMdbEnv* mev, // context + const mdbOid* inOid, // row to find in table + mdb_bool* outHasOid) // whether inOid is a member row +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( outHasOid ) + *outHasOid = MapHasOid(ev, inOid); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::CutOid( // make sure the row with inOid is not a member + nsIMdbEnv* mev, // context + const mdbOid* inOid) // row to remove from table +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( inOid && mTable_Store ) + { + morkRow* row = mTable_Store->GetRow(ev, inOid); + if ( row ) + CutRow(ev, row); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end oid set methods ----- + +// { ----- begin row set methods ----- +NS_IMETHODIMP +morkTable::NewRow( // create a new row instance in table + nsIMdbEnv* mev, // context + mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs + nsIMdbRow** acqRow) // create new row +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( ioOid && mTable_Store ) + { + morkRow* row = 0; + if ( ioOid->mOid_Id == morkRow_kMinusOneRid ) + row = mTable_Store->NewRow(ev, ioOid->mOid_Scope); + else + row = mTable_Store->NewRowWithOid(ev, ioOid); + + if ( row && AddRow(ev, row) ) + outRow = row->AcquireRowHandle(ev, mTable_Store); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkTable::AddRow( // make sure the row with inOid is a table member + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow) // row to ensure membership in table +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject *rowObj = (morkRowObject *) ioRow; + morkRow* row = rowObj->mRowObject_Row; + AddRow(ev, row); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::HasRow( // test for the table position of a row member + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_bool* outBool) // zero-based ordinal position of row in table +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject *rowObj = (morkRowObject *) ioRow; + morkRow* row = rowObj->mRowObject_Row; + if ( outBool ) + *outBool = MapHasOid(ev, &row->mRow_Oid); + outErr = ev->AsErr(); + } + return outErr; +} + + +NS_IMETHODIMP +morkTable::CutRow( // make sure the row with inOid is not a member + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow) // row to remove from table +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject *rowObj = (morkRowObject *) ioRow; + morkRow* row = rowObj->mRowObject_Row; + CutRow(ev, row); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkTable::CutAllRows( // remove all rows from the table + nsIMdbEnv* mev) // context +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + CutAllRows(ev); + outErr = ev->AsErr(); + } + return outErr; +} +// } ----- end row set methods ----- + +// { ----- begin searching methods ----- +NS_IMETHODIMP +morkTable::FindRowMatches( // search variable number of sorted cols + nsIMdbEnv* mev, // context + const mdbYarn* inPrefix, // content to find as prefix in row's column cell + nsIMdbTableRowCursor** acqCursor) // set of matching rows +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::GetSearchColumns( // query columns used by FindRowMatches() + nsIMdbEnv* mev, // context + mdb_count* outCount, // context + mdbColumnSet* outColSet) // caller supplied space to put columns + // GetSearchColumns() returns the columns actually searched when the + // FindRowMatches() method is called. No more than mColumnSet_Count + // slots of mColumnSet_Columns will be written, since mColumnSet_Count + // indicates how many slots are present in the column array. The + // actual number of search column used by the table is returned in + // the outCount parameter; if this number exceeds mColumnSet_Count, + // then a caller needs a bigger array to read the entire column set. + // The minimum of mColumnSet_Count and outCount is the number slots + // in mColumnSet_Columns that were actually written by this method. + // + // Callers are expected to change this set of columns by calls to + // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end searching methods ----- + +// { ----- begin hinting methods ----- +NS_IMETHODIMP +morkTable::SearchColumnsHint( // advise re future expected search cols + nsIMdbEnv* mev, // context + const mdbColumnSet* inColumnSet) // columns likely to be searched +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::SortColumnsHint( // advise re future expected sort columns + nsIMdbEnv* mev, // context + const mdbColumnSet* inColumnSet) // columns for likely sort requests +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::StartBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* mev, // context + const void* inLabel) // intend unique address to match end call + // If batch starts nest by virtue of nesting calls in the stack, then + // the address of a local variable makes a good batch start label that + // can be used at batch end time, and such addresses remain unique. +{ + // we don't do anything here. + return NS_OK; +} + +NS_IMETHODIMP +morkTable::EndBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* mev, // context + const void* inLabel) // label matching start label + // Suppose a table is maintaining one or many sort orders for a table, + // so that every row added to the table must be inserted in each sort, + // and every row cut must be removed from each sort. If a db client + // intends to make many such changes before needing any information + // about the order or positions of rows inside a table, then a client + // might tell the table to start batch changes in order to disable + // sorting of rows for the interim. Presumably a table will then do + // a full sort of all rows at need when the batch changes end, or when + // a surprise request occurs for row position during batch changes. +{ + // we don't do anything here. + return NS_OK; +} +// } ----- end hinting methods ----- + +// { ----- begin sorting methods ----- +// sorting: note all rows are assumed sorted by row ID as a secondary +// sort following the primary column sort, when table rows are sorted. + +NS_IMETHODIMP +morkTable::CanSortColumn( // query which column is currently used for sorting + nsIMdbEnv* mev, // context + mdb_column inColumn, // column to query sorting potential + mdb_bool* outCanSort) // whether the column can be sorted +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::GetSorting( // view same table in particular sorting + nsIMdbEnv* mev, // context + mdb_column inColumn, // requested new column for sorting table + nsIMdbSorting** acqSorting) // acquire sorting for column +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::SetSearchSorting( // use this sorting in FindRowMatches() + nsIMdbEnv* mev, // context + mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() + nsIMdbSorting* ioSorting) // requested sorting for some column + // SetSearchSorting() attempts to inform the table that ioSorting + // should be used during calls to FindRowMatches() for searching + // the column which is actually sorted by ioSorting. This method + // is most useful in conjunction with nsIMdbSorting::SetCompare(), + // because otherwise a caller would not be able to override the + // comparison ordering method used during searchs. Note that some + // database implementations might be unable to use an arbitrarily + // specified sort order, either due to schema or runtime interface + // constraints, in which case ioSorting might not actually be used. + // Presumably ioSorting is an instance that was returned from some + // earlier call to nsIMdbTable::GetSorting(). A caller can also + // use nsIMdbTable::SearchColumnsHint() to specify desired change + // in which columns are sorted and searched by FindRowMatches(). + // + // A caller can pass a nil pointer for ioSorting to request that + // column inColumn no longer be used at all by FindRowMatches(). + // But when ioSorting is non-nil, then inColumn should match the + // column actually sorted by ioSorting; when these do not agree, + // implementations are instructed to give precedence to the column + // specified by ioSorting (so this means callers might just pass + // zero for inColumn when ioSorting is also provided, since then + // inColumn is both redundant and ignored). +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// } ----- end sorting methods ----- + +// { ----- begin moving methods ----- +// moving a row does nothing unless a table is currently unsorted + +NS_IMETHODIMP +morkTable::MoveOid( // change position of row in unsorted table + nsIMdbEnv* mev, // context + const mdbOid* inOid, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row inOid + mdb_pos* outActualPos) // actual new position of row in table +{ + nsresult outErr = NS_OK; + mdb_pos actualPos = -1; // meaning it was never found in table + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( inOid && mTable_Store ) + { + morkRow* row = mTable_Store->GetRow(ev, inOid); + if ( row ) + actualPos = MoveRow(ev, row, inHintFromPos, inToPos); + } + else + ev->NilPointerError(); + + outErr = ev->AsErr(); + } + if ( outActualPos ) + *outActualPos = actualPos; + return outErr; +} + +NS_IMETHODIMP +morkTable::MoveRow( // change position of row in unsorted table + nsIMdbEnv* mev, // context + nsIMdbRow* ioRow, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row ioRow + mdb_pos* outActualPos) // actual new position of row in table +{ + mdb_pos actualPos = -1; // meaning it was never found in table + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + morkRowObject *rowObj = (morkRowObject *) ioRow; + morkRow* row = rowObj->mRowObject_Row; + actualPos = MoveRow(ev, row, inHintFromPos, inToPos); + outErr = ev->AsErr(); + } + if ( outActualPos ) + *outActualPos = actualPos; + return outErr; +} +// } ----- end moving methods ----- + +// { ----- begin index methods ----- +NS_IMETHODIMP +morkTable::AddIndex( // create a sorting index for column if possible + nsIMdbEnv* mev, // context + mdb_column inColumn, // the column to sort by index + nsIMdbThumb** acqThumb) // acquire thumb for incremental index building +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the index addition will be finished. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::CutIndex( // stop supporting a specific column index + nsIMdbEnv* mev, // context + mdb_column inColumn, // the column with index to be removed + nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy +// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and +// then the index removal will be finished. +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::HasIndex( // query for current presence of a column index + nsIMdbEnv* mev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outHasIndex) // whether column has index for this column +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::EnableIndexOnSort( // create an index for col on first sort + nsIMdbEnv* mev, // context + mdb_column inColumn) // the column to index if ever sorted +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::QueryIndexOnSort( // check whether index on sort is enabled + nsIMdbEnv* mev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +morkTable::DisableIndexOnSort( // prevent future index creation on sort + nsIMdbEnv* mev, // context + mdb_column inColumn) // the column to index if ever sorted +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} +// } ----- end index methods ----- + +// } ===== end nsIMdbTable methods ===== + +// we override these so that we'll use the xpcom add and release ref. +#ifndef _MSC_VER +mork_refs +morkTable::AddStrongRef(nsIMdbEnv *ev) +{ + return (mork_refs) AddRef(); +} +#endif + +mork_refs +morkTable::AddStrongRef(morkEnv *ev) +{ + return (mork_refs) AddRef(); +} + +#ifndef _MSC_VER +nsresult +morkTable::CutStrongRef(nsIMdbEnv *ev) +{ + return (nsresult) Release(); +} +#endif + +mork_refs +morkTable::CutStrongRef(morkEnv *ev) +{ + return (mork_refs) Release(); +} + +mork_u2 +morkTable::AddTableGcUse(morkEnv* ev) +{ + MORK_USED_1(ev); + if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not already maxed out? + ++mTable_GcUses; + + return mTable_GcUses; +} + +mork_u2 +morkTable::CutTableGcUse(morkEnv* ev) +{ + if ( mTable_GcUses ) // any outstanding uses to cut? + { + if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not frozen at max? + --mTable_GcUses; + } + else + this->TableGcUsesUnderflowWarning(ev); + + return mTable_GcUses; +} + +// table dirty handling more complex thatn morkNode::SetNodeDirty() etc. + +void morkTable::SetTableClean(morkEnv* ev) +{ + if ( mTable_ChangeList.HasListMembers() ) + { + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes + } + mTable_ChangesCount = 0; + + mTable_Flags = 0; + this->SetNodeClean(); +} + +// notifications regarding table changes: + +void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos) +{ + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + if ( this->IsTableRewrite() || this->HasChangeOverflow() ) + this->NoteTableSetAll(ev); + else + { + morkTableChange* tableChange = new(*heap, ev) + morkTableChange(ev, ioRow, inPos); + if ( tableChange ) + { + if ( ev->Good() ) + { + mTable_ChangeList.PushTail(tableChange); + ++mTable_ChangesCount; + } + else + { + tableChange->ZapOldNext(ev, heap); + this->SetTableRewrite(); // just plan to write all table rows + } + } + } +} + +void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos) +{ + if ( this->IsTableRewrite() || this->HasChangeOverflow() ) + this->NoteTableSetAll(ev); + else + { + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + morkTableChange* tableChange = new(*heap, ev) + morkTableChange(ev, ioRow, inNewPos); + if ( tableChange ) + { + if ( ev->Good() ) + { + mTable_ChangeList.PushTail(tableChange); + ++mTable_ChangesCount; + } + else + { + tableChange->ZapOldNext(ev, heap); + this->NoteTableSetAll(ev); + } + } + } +} + +void morkTable::note_row_change(morkEnv* ev, mork_change inChange, + morkRow* ioRow) +{ + if ( this->IsTableRewrite() || this->HasChangeOverflow() ) + this->NoteTableSetAll(ev); + else + { + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + morkTableChange* tableChange = new(*heap, ev) + morkTableChange(ev, inChange, ioRow); + if ( tableChange ) + { + if ( ev->Good() ) + { + mTable_ChangeList.PushTail(tableChange); + ++mTable_ChangesCount; + } + else + { + tableChange->ZapOldNext(ev, heap); + this->NoteTableSetAll(ev); + } + } + } +} + +void morkTable::NoteTableSetAll(morkEnv* ev) +{ + if ( mTable_ChangeList.HasListMembers() ) + { + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes + } + mTable_ChangesCount = 0; + this->SetTableRewrite(); +} + +/*static*/ void +morkTable::TableGcUsesUnderflowWarning(morkEnv* ev) +{ + ev->NewWarning("mTable_GcUses underflow"); +} + +/*static*/ void +morkTable::NonTableTypeError(morkEnv* ev) +{ + ev->NewError("non morkTable"); +} + +/*static*/ void +morkTable::NonTableTypeWarning(morkEnv* ev) +{ + ev->NewWarning("non morkTable"); +} + +/*static*/ void +morkTable::NilRowSpaceError(morkEnv* ev) +{ + ev->NewError("nil mTable_RowSpace"); +} + +mork_bool morkTable::MaybeDirtySpaceStoreAndTable() +{ + morkRowSpace* rowSpace = mTable_RowSpace; + if ( rowSpace ) + { + morkStore* store = rowSpace->mSpace_Store; + if ( store && store->mStore_CanDirty ) + { + store->SetStoreDirty(); + rowSpace->mSpace_CanDirty = morkBool_kTrue; + } + + if ( rowSpace->mSpace_CanDirty ) // first time being dirtied? + { + if ( this->IsTableClean() ) + { + mork_count rowCount = this->GetRowCount(); + mork_count oneThird = rowCount / 4; // one third of rows + if ( oneThird > 0x07FFF ) // more than half max u2? + oneThird = 0x07FFF; + + mTable_ChangesMax = (mork_u2) oneThird; + } + this->SetTableDirty(); + rowSpace->SetRowSpaceDirty(); + + return morkBool_kTrue; + } + } + return morkBool_kFalse; +} + +morkRow* +morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid) +{ + morkRow* outRow = mTable_MetaRow; + if ( !outRow ) + { + morkStore* store = mTable_Store; + mdbOid* oid = &mTable_MetaRowOid; + if ( inOptionalMetaRowOid && !oid->mOid_Scope ) + *oid = *inOptionalMetaRowOid; + + if ( oid->mOid_Scope ) // oid already recorded in table? + outRow = store->OidToRow(ev, oid); + else + { + outRow = store->NewRow(ev, morkStore_kMetaScope); + if ( outRow ) // need to record new oid in table? + *oid = outRow->mRow_Oid; + } + mTable_MetaRow = outRow; + if ( outRow ) // need to note another use of this row? + { + outRow->AddRowGcUse(ev); + + this->SetTableNewMeta(); + if ( this->IsTableClean() ) // catch dirty status of meta row? + this->MaybeDirtySpaceStoreAndTable(); + } + } + + return outRow; +} + +void +morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid) +{ + morkRowSpace* space = mTable_RowSpace; + if ( space ) + { + outOid->mOid_Scope = space->SpaceScope(); + outOid->mOid_Id = this->TableId(); + } + else + this->NilRowSpaceError(ev); +} + +nsIMdbTable* +morkTable::AcquireTableHandle(morkEnv* ev) +{ + AddRef(); + return this; +} + +mork_pos +morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid) +{ + MORK_USED_1(ev); + mork_count count = mTable_RowArray.mArray_Fill; + mork_pos pos = -1; + while ( ++pos < (mork_pos)count ) + { + morkRow* row = (morkRow*) mTable_RowArray.At(pos); + MORK_ASSERT(row); + if ( row && row->EqualOid(inOid) ) + { + return pos; + } + } + return -1; +} + +mork_bool +morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid) +{ + if ( mTable_RowMap ) + return ( mTable_RowMap->GetOid(ev, inOid) != 0 ); + else + return ( ArrayHasOid(ev, inOid) >= 0 ); +} + +void morkTable::build_row_map(morkEnv* ev) +{ + morkRowMap* map = mTable_RowMap; + if ( !map ) + { + mork_count count = mTable_RowArray.mArray_Fill + 3; + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + map = new(*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count); + if ( map ) + { + if ( ev->Good() ) + { + mTable_RowMap = map; // put strong ref here + count = mTable_RowArray.mArray_Fill; + mork_pos pos = -1; + while ( ++pos < (mork_pos)count ) + { + morkRow* row = (morkRow*) mTable_RowArray.At(pos); + if ( row && row->IsRow() ) + map->AddRow(ev, row); + else + row->NonRowTypeError(ev); + } + } + else + map->CutStrongRef(ev); + } + } +} + +morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow) +{ + if ( mTable_RowMap ) + return mTable_RowMap->GetRow(ev, ioRow); + else + { + mork_count count = mTable_RowArray.mArray_Fill; + mork_pos pos = -1; + while ( ++pos < (mork_pos)count ) + { + morkRow* row = (morkRow*) mTable_RowArray.At(pos); + if ( row == ioRow ) + return row; + } + } + return (morkRow*) 0; +} + +mork_pos +morkTable::MoveRow(morkEnv* ev, morkRow* ioRow, // change row position + mork_pos inHintFromPos, // suggested hint regarding start position + mork_pos inToPos) // desired new position for row ioRow + // MoveRow() returns the actual position of ioRow afterwards; this + // position is -1 if and only if ioRow was not found as a member. +{ + mork_pos outPos = -1; // means ioRow was not a table member + mork_bool canDirty = ( this->IsTableClean() )? + this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; + + morkRow** rows = (morkRow**) mTable_RowArray.mArray_Slots; + mork_count count = mTable_RowArray.mArray_Fill; + if ( count && rows && ev->Good() ) // any members at all? no errors? + { + mork_pos lastPos = count - 1; // index of last row slot + + if ( inToPos > lastPos ) // beyond last used array slot? + inToPos = lastPos; // put row into last available slot + else if ( inToPos < 0 ) // before first usable slot? + inToPos = 0; // put row in very first slow + + if ( inHintFromPos > lastPos ) // beyond last used array slot? + inHintFromPos = lastPos; // seek row in last available slot + else if ( inHintFromPos < 0 ) // before first usable slot? + inHintFromPos = 0; // seek row in very first slow + + morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found + morkRow** rowsEnd = rows + count; // one past last used array slot + + if ( inHintFromPos <= 0 ) // start of table? just scan for row? + { + morkRow** cursor = rows - 1; // before first array slot + while ( ++cursor < rowsEnd ) + { + if ( *cursor == ioRow ) + { + fromSlot = cursor; + break; // end while loop + } + } + } + else // search near the start position and work outwards + { + morkRow** lo = rows + inHintFromPos; // lowest search point + morkRow** hi = lo; // highest search point starts at lowest point + + // Seek ioRow in spiral widening search below and above inHintFromPos. + // This is faster when inHintFromPos is at all accurate, but is slower + // than a straightforward scan when inHintFromPos is nearly random. + + while ( lo >= rows || hi < rowsEnd ) // keep searching? + { + if ( lo >= rows ) // low direction search still feasible? + { + if ( *lo == ioRow ) // actually found the row? + { + fromSlot = lo; + break; // end while loop + } + --lo; // advance further lower + } + if ( hi < rowsEnd ) // high direction search still feasible? + { + if ( *hi == ioRow ) // actually found the row? + { + fromSlot = hi; + break; // end while loop + } + ++hi; // advance further higher + } + } + } + + if ( fromSlot ) // ioRow was found as a table member? + { + outPos = fromSlot - rows; // actual position where row was found + if ( outPos != inToPos ) // actually need to move this row? + { + morkRow** toSlot = rows + inToPos; // slot where row must go + + ++mTable_RowArray.mArray_Seed; // we modify the array now: + + if ( fromSlot < toSlot ) // row is moving upwards? + { + morkRow** up = fromSlot; // leading pointer going upward + while ( ++up <= toSlot ) // have not gone above destination? + { + *fromSlot = *up; // shift down one + fromSlot = up; // shift trailing pointer up + } + } + else // ( fromSlot > toSlot ) // row is moving downwards + { + morkRow** down = fromSlot; // leading pointer going downward + while ( --down >= toSlot ) // have not gone below destination? + { + *fromSlot = *down; // shift up one + fromSlot = down; // shift trailing pointer + } + } + *toSlot = ioRow; + outPos = inToPos; // okay, we actually moved the row here + + if ( canDirty ) + this->note_row_move(ev, ioRow, inToPos); + } + } + } + return outPos; +} + +mork_bool +morkTable::AddRow(morkEnv* ev, morkRow* ioRow) +{ + morkRow* row = this->find_member_row(ev, ioRow); + if ( !row && ev->Good() ) + { + mork_bool canDirty = ( this->IsTableClean() )? + this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; + + mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow); + if ( ev->Good() && pos >= 0 ) + { + ioRow->AddRowGcUse(ev); + if ( mTable_RowMap ) + { + if ( mTable_RowMap->AddRow(ev, ioRow) ) + { + // okay, anything else? + } + else + mTable_RowArray.CutSlot(ev, pos); + } + else if ( mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold ) + this->build_row_map(ev); + + if ( canDirty && ev->Good() ) + this->NoteTableAddRow(ev, ioRow); + } + } + return ev->Good(); +} + +mork_bool +morkTable::CutRow(morkEnv* ev, morkRow* ioRow) +{ + morkRow* row = this->find_member_row(ev, ioRow); + if ( row ) + { + mork_bool canDirty = ( this->IsTableClean() )? + this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue; + + mork_count count = mTable_RowArray.mArray_Fill; + morkRow** rowSlots = (morkRow**) mTable_RowArray.mArray_Slots; + if ( rowSlots ) // array has vector as expected? + { + mork_pos pos = -1; + morkRow** end = rowSlots + count; + morkRow** slot = rowSlots - 1; // prepare for preincrement: + while ( ++slot < end ) // another slot to check? + { + if ( *slot == row ) // found the slot containing row? + { + pos = slot - rowSlots; // record absolute position + break; // end while loop + } + } + if ( pos >= 0 ) // need to cut if from the array? + mTable_RowArray.CutSlot(ev, pos); + else + ev->NewWarning("row not found in array"); + } + else + mTable_RowArray.NilSlotsAddressError(ev); + + if ( mTable_RowMap ) + mTable_RowMap->CutRow(ev, ioRow); + + if ( canDirty ) + this->NoteTableCutRow(ev, ioRow); + + if ( ioRow->CutRowGcUse(ev) == 0 ) + ioRow->OnZeroRowGcUse(ev); + } + return ev->Good(); +} + + +mork_bool +morkTable::CutAllRows(morkEnv* ev) +{ + if ( this->MaybeDirtySpaceStoreAndTable() ) + { + this->SetTableRewrite(); // everything is dirty + this->NoteTableSetAll(ev); + } + + if ( ev->Good() ) + { + mTable_RowArray.CutAllSlots(ev); + if ( mTable_RowMap ) + { + morkRowMapIter i(ev, mTable_RowMap); + mork_change* c = 0; + morkRow* r = 0; + + for ( c = i.FirstRow(ev, &r); c; c = i.NextRow(ev, &r) ) + { + if ( r ) + { + if ( r->CutRowGcUse(ev) == 0 ) + r->OnZeroRowGcUse(ev); + + i.CutHereRow(ev, (morkRow**) 0); + } + else + ev->NewWarning("nil row in table map"); + } + } + } + return ev->Good(); +} + +morkTableRowCursor* +morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos) +{ + morkTableRowCursor* outCursor = 0; + if ( ev->Good() ) + { + nsIMdbHeap* heap = mTable_Store->mPort_Heap; + morkTableRowCursor* cursor = new(*heap, ev) + morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos); + if ( cursor ) + { + if ( ev->Good() ) + outCursor = cursor; + else + cursor->CutStrongRef((nsIMdbEnv *) ev); + } + } + return outCursor; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange, + morkRow* ioRow) +// use this constructor for inChange == morkChange_kAdd or morkChange_kCut +: morkNext() +, mTableChange_Row( ioRow ) +, mTableChange_Pos( morkTableChange_kNone ) +{ + if ( ioRow ) + { + if ( ioRow->IsRow() ) + { + if ( inChange == morkChange_kAdd ) + mTableChange_Pos = morkTableChange_kAdd; + else if ( inChange == morkChange_kCut ) + mTableChange_Pos = morkTableChange_kCut; + else + this->UnknownChangeError(ev); + } + else + ioRow->NonRowTypeError(ev); + } + else + ev->NilPointerError(); +} + +morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos) +// use this constructor when the row is moved +: morkNext() +, mTableChange_Row( ioRow ) +, mTableChange_Pos( inPos ) +{ + if ( ioRow ) + { + if ( ioRow->IsRow() ) + { + if ( inPos < 0 ) + this->NegativeMovePosError(ev); + } + else + ioRow->NonRowTypeError(ev); + } + else + ev->NilPointerError(); +} + +void morkTableChange::UnknownChangeError(morkEnv* ev) const +// morkChange_kAdd or morkChange_kCut +{ + ev->NewError("mTableChange_Pos neither kAdd nor kCut"); +} + +void morkTableChange::NegativeMovePosError(morkEnv* ev) const +// move must be non-neg position +{ + ev->NewError("negative mTableChange_Pos for row move"); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +morkTableMap::~morkTableMap() +{ +} + +morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) +#ifdef MORK_BEAD_OVER_NODE_MAPS + : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kTableMap; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkTable.h b/db/mork/src/morkTable.h new file mode 100644 index 0000000000..c9428fd964 --- /dev/null +++ b/db/mork/src/morkTable.h @@ -0,0 +1,729 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKTABLE_ +#define _MORKTABLE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +#ifndef _MORKARRAY_ +#include "morkArray.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKNODEMAP_ +#include "morkNodeMap.h" +#endif + +#ifndef _MORKPROBEMAP_ +#include "morkProbeMap.h" +#endif + +#ifndef _MORKBEAD_ +#include "morkBead.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class nsIMdbTable; +#define morkDerived_kTable /*i*/ 0x5462 /* ascii 'Tb' */ + +/*| kStartRowArraySize: starting physical size of array for mTable_RowArray. +**| We want this number very small, so that a table containing exactly one +**| row member will not pay too significantly in space overhead. But we want +**| a number bigger than one, so there is some space for growth. +|*/ +#define morkTable_kStartRowArraySize 3 /* modest starting size for array */ + +/*| kMakeRowMapThreshold: this is the number of rows in a table which causes +**| a hash table (mTable_RowMap) to be lazily created for faster member row +**| identification, during such operations as cuts and adds. This number must +**| be small enough that linear searches are not bad for member counts less +**| than this; but this number must also be large enough that creating a hash +**| table does not increase the per-row space overhead by a big percentage. +**| For speed, numbers on the order of ten to twenty are all fine; for space, +**| I believe a number as small as ten will have too much space overhead. +|*/ +#define morkTable_kMakeRowMapThreshold 17 /* when to build mTable_RowMap */ + +#define morkTable_kStartRowMapSlotCount 13 +#define morkTable_kMaxTableGcUses 0x0FF /* max for 8-bit unsigned int */ + +#define morkTable_kUniqueBit ((mork_u1) (1 << 0)) +#define morkTable_kVerboseBit ((mork_u1) (1 << 1)) +#define morkTable_kNotedBit ((mork_u1) (1 << 2)) /* space has change notes */ +#define morkTable_kRewriteBit ((mork_u1) (1 << 3)) /* must rewrite all rows */ +#define morkTable_kNewMetaBit ((mork_u1) (1 << 4)) /* new table meta row */ + +class morkTable : public morkObject, public morkLink, public nsIMdbTable { + + // NOTE the morkLink base is for morkRowSpace::mRowSpace_TablesByPriority + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // bead color setter & getter replace obsolete member mTable_Id: + + NS_DECL_ISUPPORTS_INHERITED + mork_tid TableId() const { return mBead_Color; } + void SetTableId(mork_tid inTid) { mBead_Color = inTid; } + + // we override these so we use xpcom ref-counting semantics. +#ifndef _MSC_VER + // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual. + virtual mork_refs AddStrongRef(nsIMdbEnv* ev) override; +#endif + virtual mork_refs AddStrongRef(morkEnv* ev) override; +#ifndef _MSC_VER + // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual. + virtual nsresult CutStrongRef(nsIMdbEnv* ev) override; +#endif + virtual mork_refs CutStrongRef(morkEnv* ev) override; +public: // state is public because the entire Mork system is private + +// { ===== begin nsIMdbCollection methods ===== + + // { ----- begin attribute methods ----- + NS_IMETHOD GetSeed(nsIMdbEnv* ev, + mdb_seed* outSeed) override; // member change count + NS_IMETHOD GetCount(nsIMdbEnv* ev, + mdb_count* outCount) override; // member count + + NS_IMETHOD GetPort(nsIMdbEnv* ev, + nsIMdbPort** acqPort) override; // collection container + // } ----- end attribute methods ----- + + // { ----- begin cursor methods ----- + NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos + nsIMdbEnv* ev, // context + mdb_pos inMemberPos, // zero-based ordinal pos of member in collection + nsIMdbCursor** acqCursor) override; // acquire new cursor instance + // } ----- end cursor methods ----- + + // { ----- begin ID methods ----- + NS_IMETHOD GetOid(nsIMdbEnv* ev, + mdbOid* outOid) override; // read object identity + NS_IMETHOD BecomeContent(nsIMdbEnv* ev, + const mdbOid* inOid) override; // exchange content + // } ----- end ID methods ----- + + // { ----- begin activity dropping methods ----- + NS_IMETHOD DropActivity( // tell collection usage no longer expected + nsIMdbEnv* ev) override; + // } ----- end activity dropping methods ----- + +// } ===== end nsIMdbCollection methods ===== + NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) override; + NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) override; + + NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) override; + NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) override; + + NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) override; + + NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override; + NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override; + + NS_IMETHOD GetMetaRow( + nsIMdbEnv* ev, // context + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mdbOid* outOid, // output meta row oid, can be nil to suppress output + nsIMdbRow** acqRow) override; // acquire table's unique singleton meta row + // The purpose of a meta row is to support the persistent recording of + // meta info about a table as cells put into the distinguished meta row. + // Each table has exactly one meta row, which is not considered a member + // of the collection of rows inside the table. The only way to tell + // whether a row is a meta row is by the fact that it is returned by this + // GetMetaRow() method from some table. Otherwise nothing distinguishes + // a meta row from any other row. A meta row can be used anyplace that + // any other row can be used, and can even be put into other tables (or + // the same table) as a table member, if this is useful for some reason. + // The first attempt to access a table's meta row using GetMetaRow() will + // cause the meta row to be created if it did not already exist. When the + // meta row is created, it will have the row oid that was previously + // requested for this table's meta row; or if no oid was ever explicitly + // specified for this meta row, then a unique oid will be generated in + // the row scope named "m" (so obviously MDB clients should not + // manually allocate any row IDs from that special meta scope namespace). + // The meta row oid can be specified either when the table is created, or + // else the first time that GetMetaRow() is called, by passing a non-nil + // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's + // actual oid is returned in outOid (if this is a non-nil pointer), and + // it will be different from inOptionalMetaRowOid when the meta row was + // already given a different oid earlier. + // } ----- end meta attribute methods ----- + + + // { ----- begin cursor methods ----- + NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbTableRowCursor** acqCursor) override; // acquire new cursor instance + // } ----- end row position methods ----- + + // { ----- begin row position methods ----- + NS_IMETHOD PosToOid( // get row member for a table position + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + mdbOid* outOid) override; // row oid at the specified position + + NS_IMETHOD OidToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_pos* outPos) override; // zero-based ordinal position of row in table + + NS_IMETHOD PosToRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + mdb_pos inRowPos, // zero-based ordinal position of row in table + nsIMdbRow** acqRow) override; // acquire row at table position inRowPos + + NS_IMETHOD RowToPos( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_pos* outPos) override; // zero-based ordinal position of row in table + // } ----- end row position methods ----- + + // { ----- begin oid set methods ----- + NS_IMETHOD AddOid( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + const mdbOid* inOid) override; // row to ensure membership in table + + NS_IMETHOD HasOid( // test for the table position of a row member + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row to find in table + mdb_bool* outHasOid) override; // whether inOid is a member row + + NS_IMETHOD CutOid( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + const mdbOid* inOid) override; // row to remove from table + // } ----- end oid set methods ----- + + // { ----- begin row set methods ----- + NS_IMETHOD NewRow( // create a new row instance in table + nsIMdbEnv* ev, // context + mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs + nsIMdbRow** acqRow) override; // create new row + + NS_IMETHOD AddRow( // make sure the row with inOid is a table member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) override; // row to ensure membership in table + + NS_IMETHOD HasRow( // test for the table position of a row member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row to find in table + mdb_bool* outHasRow) override; // whether row is a table member + + NS_IMETHOD CutRow( // make sure the row with inOid is not a member + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow) override; // row to remove from table + + NS_IMETHOD CutAllRows( // remove all rows from the table + nsIMdbEnv* ev) override; // context + // } ----- end row set methods ----- + + // { ----- begin hinting methods ----- + NS_IMETHOD SearchColumnsHint( // advise re future expected search cols + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) override; // columns likely to be searched + + NS_IMETHOD SortColumnsHint( // advise re future expected sort columns + nsIMdbEnv* ev, // context + const mdbColumnSet* inColumnSet) override; // columns for likely sort requests + + NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) override; // intend unique address to match end call + // If batch starts nest by virtue of nesting calls in the stack, then + // the address of a local variable makes a good batch start label that + // can be used at batch end time, and such addresses remain unique. + + NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts + nsIMdbEnv* ev, // context + const void* inLabel) override; // label matching start label + // Suppose a table is maintaining one or many sort orders for a table, + // so that every row added to the table must be inserted in each sort, + // and every row cut must be removed from each sort. If a db client + // intends to make many such changes before needing any information + // about the order or positions of rows inside a table, then a client + // might tell the table to start batch changes in order to disable + // sorting of rows for the interim. Presumably a table will then do + // a full sort of all rows at need when the batch changes end, or when + // a surprise request occurs for row position during batch changes. + // } ----- end hinting methods ----- + + // { ----- begin searching methods ----- + NS_IMETHOD FindRowMatches( // search variable number of sorted cols + nsIMdbEnv* ev, // context + const mdbYarn* inPrefix, // content to find as prefix in row's column cell + nsIMdbTableRowCursor** acqCursor) override; // set of matching rows + + NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches() + nsIMdbEnv* ev, // context + mdb_count* outCount, // context + mdbColumnSet* outColSet) override; // caller supplied space to put columns + // GetSearchColumns() returns the columns actually searched when the + // FindRowMatches() method is called. No more than mColumnSet_Count + // slots of mColumnSet_Columns will be written, since mColumnSet_Count + // indicates how many slots are present in the column array. The + // actual number of search column used by the table is returned in + // the outCount parameter; if this number exceeds mColumnSet_Count, + // then a caller needs a bigger array to read the entire column set. + // The minimum of mColumnSet_Count and outCount is the number slots + // in mColumnSet_Columns that were actually written by this method. + // + // Callers are expected to change this set of columns by calls to + // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both. + // } ----- end searching methods ----- + + // { ----- begin sorting methods ----- + // sorting: note all rows are assumed sorted by row ID as a secondary + // sort following the primary column sort, when table rows are sorted. + + NS_IMETHOD + CanSortColumn( // query which column is currently used for sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // column to query sorting potential + mdb_bool* outCanSort) override; // whether the column can be sorted + + NS_IMETHOD GetSorting( // view same table in particular sorting + nsIMdbEnv* ev, // context + mdb_column inColumn, // requested new column for sorting table + nsIMdbSorting** acqSorting) override; // acquire sorting for column + + NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches() + nsIMdbEnv* ev, // context + mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn() + nsIMdbSorting* ioSorting) override; // requested sorting for some column + // SetSearchSorting() attempts to inform the table that ioSorting + // should be used during calls to FindRowMatches() for searching + // the column which is actually sorted by ioSorting. This method + // is most useful in conjunction with nsIMdbSorting::SetCompare(), + // because otherwise a caller would not be able to override the + // comparison ordering method used during searchs. Note that some + // database implementations might be unable to use an arbitrarily + // specified sort order, either due to schema or runtime interface + // constraints, in which case ioSorting might not actually be used. + // Presumably ioSorting is an instance that was returned from some + // earlier call to nsIMdbTable::GetSorting(). A caller can also + // use nsIMdbTable::SearchColumnsHint() to specify desired change + // in which columns are sorted and searched by FindRowMatches(). + // + // A caller can pass a nil pointer for ioSorting to request that + // column inColumn no longer be used at all by FindRowMatches(). + // But when ioSorting is non-nil, then inColumn should match the + // column actually sorted by ioSorting; when these do not agree, + // implementations are instructed to give precedence to the column + // specified by ioSorting (so this means callers might just pass + // zero for inColumn when ioSorting is also provided, since then + // inColumn is both redundant and ignored). + // } ----- end sorting methods ----- + + // { ----- begin moving methods ----- + // moving a row does nothing unless a table is currently unsorted + + NS_IMETHOD MoveOid( // change position of row in unsorted table + nsIMdbEnv* ev, // context + const mdbOid* inOid, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row inRowId + mdb_pos* outActualPos) override; // actual new position of row in table + + NS_IMETHOD MoveRow( // change position of row in unsorted table + nsIMdbEnv* ev, // context + nsIMdbRow* ioRow, // row oid to find in table + mdb_pos inHintFromPos, // suggested hint regarding start position + mdb_pos inToPos, // desired new position for row inRowId + mdb_pos* outActualPos) override; // actual new position of row in table + // } ----- end moving methods ----- + + // { ----- begin index methods ----- + NS_IMETHOD AddIndex( // create a sorting index for column if possible + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to sort by index + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental index building + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the index addition will be finished. + + NS_IMETHOD CutIndex( // stop supporting a specific column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column with index to be removed + nsIMdbThumb** acqThumb) override; // acquire thumb for incremental index destroy + // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and + // then the index removal will be finished. + + NS_IMETHOD HasIndex( // query for current presence of a column index + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outHasIndex) override; // whether column has index for this column + + + NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // the column to index if ever sorted + + NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled + nsIMdbEnv* ev, // context + mdb_column inColumn, // the column to investigate + mdb_bool* outIndexOnSort) override; // whether column has index-on-sort enabled + + NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort + nsIMdbEnv* ev, // context + mdb_column inColumn) override; // the column to index if ever sorted + // } ----- end index methods ----- + + morkStore* mTable_Store; // non-refcnted ptr to port + + // mTable_RowSpace->SpaceScope() is row scope + morkRowSpace* mTable_RowSpace; // non-refcnted ptr to containing space + + morkRow* mTable_MetaRow; // table's actual meta row + mdbOid mTable_MetaRowOid; // oid for meta row + + morkRowMap* mTable_RowMap; // (strong ref) hash table of all members + morkArray mTable_RowArray; // array of morkRow pointers + + morkList mTable_ChangeList; // list of table changes + mork_u2 mTable_ChangesCount; // length of changes list + mork_u2 mTable_ChangesMax; // max list length before rewrite + + // mork_tid mTable_Id; + mork_kind mTable_Kind; + + mork_u1 mTable_Flags; // bit flags + mork_priority mTable_Priority; // 0..9, any other value equals 9 + mork_u1 mTable_GcUses; // persistent references from cells + mork_u1 mTable_Pad; // for u4 alignment + +public: // flags bit twiddling + + void SetTableUnique() { mTable_Flags |= morkTable_kUniqueBit; } + void SetTableVerbose() { mTable_Flags |= morkTable_kVerboseBit; } + void SetTableNoted() { mTable_Flags |= morkTable_kNotedBit; } + void SetTableRewrite() { mTable_Flags |= morkTable_kRewriteBit; } + void SetTableNewMeta() { mTable_Flags |= morkTable_kNewMetaBit; } + + void ClearTableUnique() { mTable_Flags &= (mork_u1) ~morkTable_kUniqueBit; } + void ClearTableVerbose() { mTable_Flags &= (mork_u1) ~morkTable_kVerboseBit; } + void ClearTableNoted() { mTable_Flags &= (mork_u1) ~morkTable_kNotedBit; } + void ClearTableRewrite() { mTable_Flags &= (mork_u1) ~morkTable_kRewriteBit; } + void ClearTableNewMeta() { mTable_Flags &= (mork_u1) ~morkTable_kNewMetaBit; } + + mork_bool IsTableUnique() const + { return ( mTable_Flags & morkTable_kUniqueBit ) != 0; } + + mork_bool IsTableVerbose() const + { return ( mTable_Flags & morkTable_kVerboseBit ) != 0; } + + mork_bool IsTableNoted() const + { return ( mTable_Flags & morkTable_kNotedBit ) != 0; } + + mork_bool IsTableRewrite() const + { return ( mTable_Flags & morkTable_kRewriteBit ) != 0; } + + mork_bool IsTableNewMeta() const + { return ( mTable_Flags & morkTable_kNewMetaBit ) != 0; } + +public: // table dirty handling more complex than morkNode::SetNodeDirty() etc. + + void SetTableDirty() { this->SetNodeDirty(); } + void SetTableClean(morkEnv* ev); + + mork_bool IsTableClean() const { return this->IsNodeClean(); } + mork_bool IsTableDirty() const { return this->IsNodeDirty(); } + +public: // morkNode memory management operators + void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW + { return morkNode::MakeNew(inSize, ioHeap, ev); } + + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseTable() if open + +public: // morkTable construction & destruction + morkTable(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, morkStore* ioStore, + nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace, + const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying + mork_tid inTableId, + mork_kind inKind, mork_bool inMustBeUnique); + void CloseTable(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkTable(const morkTable& other); + morkTable& operator=(const morkTable& other); + virtual ~morkTable(); // assert that close executed earlier + +public: // dynamic type identification + mork_bool IsTable() const + { return IsNode() && mNode_Derived == morkDerived_kTable; } +// } ===== end morkNode methods ===== + +public: // errors + static void NonTableTypeError(morkEnv* ev); + static void NonTableTypeWarning(morkEnv* ev); + static void NilRowSpaceError(morkEnv* ev); + +public: // warnings + static void TableGcUsesUnderflowWarning(morkEnv* ev); + +public: // noting table changes + + mork_bool HasChangeOverflow() const + { return mTable_ChangesCount >= mTable_ChangesMax; } + + void NoteTableSetAll(morkEnv* ev); + void NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos); + + void note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow); + void note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos); + + void NoteTableAddRow(morkEnv* ev, morkRow* ioRow) + { this->note_row_change(ev, morkChange_kAdd, ioRow); } + + void NoteTableCutRow(morkEnv* ev, morkRow* ioRow) + { this->note_row_change(ev, morkChange_kCut, ioRow); } + +protected: // internal row map methods + + morkRow* find_member_row(morkEnv* ev, morkRow* ioRow); + void build_row_map(morkEnv* ev); + +public: // other table methods + + mork_bool MaybeDirtySpaceStoreAndTable(); + + morkRow* GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid); + + mork_u2 AddTableGcUse(morkEnv* ev); + mork_u2 CutTableGcUse(morkEnv* ev); + + // void DirtyAllTableContent(morkEnv* ev); + + mork_seed TableSeed() const { return mTable_RowArray.mArray_Seed; } + + morkRow* SafeRowAt(morkEnv* ev, mork_pos inPos) + { return (morkRow*) mTable_RowArray.SafeAt(ev, inPos); } + + nsIMdbTable* AcquireTableHandle(morkEnv* ev); // mObject_Handle + + mork_count GetRowCount() const { return mTable_RowArray.mArray_Fill; } + + mork_bool IsTableUsed() const + { return (mTable_GcUses != 0 || this->GetRowCount() != 0); } + + void GetTableOid(morkEnv* ev, mdbOid* outOid); + mork_pos ArrayHasOid(morkEnv* ev, const mdbOid* inOid); + mork_bool MapHasOid(morkEnv* ev, const mdbOid* inOid); + mork_bool AddRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good() + mork_bool CutRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good() + mork_bool CutAllRows(morkEnv* ev); // returns ev->Good() + + mork_pos MoveRow(morkEnv* ev, morkRow* ioRow, // change row position + mork_pos inHintFromPos, // suggested hint regarding start position + mork_pos inToPos); // desired new position for row ioRow + // MoveRow() returns the actual position of ioRow afterwards; this + // position is -1 if and only if ioRow was not found as a member. + + + morkTableRowCursor* NewTableRowCursor(morkEnv* ev, mork_pos inRowPos); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakTable(morkTable* me, + morkEnv* ev, morkTable** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongTable(morkTable* me, + morkEnv* ev, morkTable** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// use negative values for kCut and kAdd, to keep non-neg move pos distinct: +#define morkTableChange_kCut ((mork_pos) -1) /* shows row was cut */ +#define morkTableChange_kAdd ((mork_pos) -2) /* shows row was added */ +#define morkTableChange_kNone ((mork_pos) -3) /* unknown change */ + +class morkTableChange : public morkNext { +public: // state is public because the entire Mork system is private + + morkRow* mTableChange_Row; // the row in the change + + mork_pos mTableChange_Pos; // kAdd, kCut, or non-neg for row move + +public: + morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow); + // use this constructor for inChange == morkChange_kAdd or morkChange_kCut + + morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos); + // use this constructor when the row is moved + +public: + void UnknownChangeError(morkEnv* ev) const; // morkChange_kAdd or morkChange_kCut + void NegativeMovePosError(morkEnv* ev) const; // move must be non-neg position + +public: + + mork_bool IsAddRowTableChange() const + { return ( mTableChange_Pos == morkTableChange_kAdd ); } + + mork_bool IsCutRowTableChange() const + { return ( mTableChange_Pos == morkTableChange_kCut ); } + + mork_bool IsMoveRowTableChange() const + { return ( mTableChange_Pos >= 0 ); } + +public: + + mork_pos GetMovePos() const { return mTableChange_Pos; } + // GetMovePos() assumes that IsMoveRowTableChange() is true. +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkDerived_kTableMap /*i*/ 0x744D /* ascii 'tM' */ + +/*| morkTableMap: maps mork_token -> morkTable +|*/ +#ifdef MORK_BEAD_OVER_NODE_MAPS +class morkTableMap : public morkBeadMap { +#else /*MORK_BEAD_OVER_NODE_MAPS*/ +class morkTableMap : public morkNodeMap { // for mapping tokens to tables +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + +public: + + virtual ~morkTableMap(); + morkTableMap(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap); + +public: // other map methods + +#ifdef MORK_BEAD_OVER_NODE_MAPS + mork_bool AddTable(morkEnv* ev, morkTable* ioTable) + { return this->AddBead(ev, ioTable); } + // the AddTable() boolean return equals ev->Good(). + + mork_bool CutTable(morkEnv* ev, mork_tid inTid) + { return this->CutBead(ev, inTid); } + // The CutTable() boolean return indicates whether removal happened. + + morkTable* GetTable(morkEnv* ev, mork_tid inTid) + { return (morkTable*) this->GetBead(ev, inTid); } + // Note the returned table does NOT have an increase in refcount for this. + + mork_num CutAllTables(morkEnv* ev) + { return this->CutAllBeads(ev); } + // CutAllTables() releases all the referenced table values. + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_bool AddTable(morkEnv* ev, morkTable* ioTable) + { return this->AddNode(ev, ioTable->TableId(), ioTable); } + // the AddTable() boolean return equals ev->Good(). + + mork_bool CutTable(morkEnv* ev, mork_tid inTid) + { return this->CutNode(ev, inTid); } + // The CutTable() boolean return indicates whether removal happened. + + morkTable* GetTable(morkEnv* ev, mork_tid inTid) + { return (morkTable*) this->GetNode(ev, inTid); } + // Note the returned table does NOT have an increase in refcount for this. + + mork_num CutAllTables(morkEnv* ev) + { return this->CutAllNodes(ev); } + // CutAllTables() releases all the referenced table values. +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + +}; + +#ifdef MORK_BEAD_OVER_NODE_MAPS +class morkTableMapIter: public morkBeadMapIter { +#else /*MORK_BEAD_OVER_NODE_MAPS*/ +class morkTableMapIter: public morkMapIter{ // typesafe wrapper class +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + +public: + +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTableMapIter(morkEnv* ev, morkTableMap* ioMap) + : morkBeadMapIter(ev, ioMap) { } + + morkTableMapIter( ) : morkBeadMapIter() { } + void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) + { this->InitBeadMapIter(ev, ioMap); } + + morkTable* FirstTable(morkEnv* ev) + { return (morkTable*) this->FirstBead(ev); } + + morkTable* NextTable(morkEnv* ev) + { return (morkTable*) this->NextBead(ev); } + + morkTable* HereTable(morkEnv* ev) + { return (morkTable*) this->HereBead(ev); } + + +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + morkTableMapIter(morkEnv* ev, morkTableMap* ioMap) + : morkMapIter(ev, ioMap) { } + + morkTableMapIter( ) : morkMapIter() { } + void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap) + { this->InitMapIter(ev, ioMap); } + + mork_change* + FirstTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) + { return this->First(ev, outTid, outTable); } + + mork_change* + NextTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) + { return this->Next(ev, outTid, outTable); } + + mork_change* + HereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) + { return this->Here(ev, outTid, outTable); } + + // cutting while iterating hash map might dirty the parent table: + mork_change* + CutHereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable) + { return this->CutHere(ev, outTid, outTable); } +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLE_ */ + diff --git a/db/mork/src/morkTableRowCursor.cpp b/db/mork/src/morkTableRowCursor.cpp new file mode 100644 index 0000000000..6f36932695 --- /dev/null +++ b/db/mork/src/morkTableRowCursor.cpp @@ -0,0 +1,493 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKTABLEROWCURSOR_ +#include "morkTableRowCursor.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkTableRowCursor::CloseMorkNode(morkEnv* ev) // CloseTableRowCursor() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseTableRowCursor(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkTableRowCursor::~morkTableRowCursor() // CloseTableRowCursor() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(this->IsShutNode()); +} + +/*public non-poly*/ +morkTableRowCursor::morkTableRowCursor(morkEnv* ev, + const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos) +: morkCursor(ev, inUsage, ioHeap) +, mTableRowCursor_Table( 0 ) +{ + if ( ev->Good() ) + { + if ( ioTable ) + { + mCursor_Pos = inRowPos; + mCursor_Seed = ioTable->TableSeed(); + morkTable::SlotWeakTable(ioTable, ev, &mTableRowCursor_Table); + if ( ev->Good() ) + mNode_Derived = morkDerived_kTableRowCursor; + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkTableRowCursor, morkCursor, nsIMdbTableRowCursor) +/*public non-poly*/ void +morkTableRowCursor::CloseTableRowCursor(morkEnv* ev) +{ + if ( this->IsNode() ) + { + mCursor_Pos = -1; + mCursor_Seed = 0; + morkTable::SlotWeakTable((morkTable*) 0, ev, &mTableRowCursor_Table); + this->CloseCursor(ev); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` +// { ----- begin attribute methods ----- +/*virtual*/ nsresult +morkTableRowCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount) +{ + nsresult outErr = NS_OK; + mdb_count count = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + count = GetMemberCount(ev); + outErr = ev->AsErr(); + } + if ( outCount ) + *outCount = count; + return outErr; +} + +/*virtual*/ nsresult +morkTableRowCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed) +{ + NS_ASSERTION(false, "not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/*virtual*/ nsresult +morkTableRowCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos) +{ + mCursor_Pos = inPos; + return NS_OK; +} + +/*virtual*/ nsresult +morkTableRowCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos) +{ + *outPos = mCursor_Pos; + return NS_OK; +} + +/*virtual*/ nsresult +morkTableRowCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail) +{ + mCursor_DoFailOnSeedOutOfSync = inFail; + return NS_OK; +} + +/*virtual*/ nsresult +morkTableRowCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail) +{ + NS_ENSURE_ARG_POINTER(outFail); + *outFail = mCursor_DoFailOnSeedOutOfSync; + return NS_OK; +} +// } ----- end attribute methods ----- + + +// { ===== begin nsIMdbTableRowCursor methods ===== + +// { ----- begin attribute methods ----- + +NS_IMETHODIMP +morkTableRowCursor::GetTable(nsIMdbEnv* mev, nsIMdbTable** acqTable) +{ + nsresult outErr = NS_OK; + nsIMdbTable* outTable = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( mTableRowCursor_Table ) + outTable = mTableRowCursor_Table->AcquireTableHandle(ev); + + outErr = ev->AsErr(); + } + if ( acqTable ) + *acqTable = outTable; + return outErr; +} +// } ----- end attribute methods ----- + +// { ----- begin oid iteration methods ----- +NS_IMETHODIMP +morkTableRowCursor::NextRowOid( // get row id of next row in the table + nsIMdbEnv* mev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) +{ + nsresult outErr = NS_OK; + mork_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( outOid ) + { + pos = NextRowOid(ev, outOid); + } + else + ev->NilPointerError(); + outErr = ev->AsErr(); + } + if ( outRowPos ) + *outRowPos = pos; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* mev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) +{ + nsresult outErr = NS_OK; + mork_pos pos = -1; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + if ( outOid ) + { + pos = PrevRowOid(ev, outOid); + } + else + ev->NilPointerError(); + outErr = ev->AsErr(); + } + if ( outRowPos ) + *outRowPos = pos; + return outErr; +} +// } ----- end oid iteration methods ----- + +// { ----- begin row iteration methods ----- +NS_IMETHODIMP +morkTableRowCursor::NextRow( // get row cells from table for cells already in row + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + + mdbOid oid; // place to put oid we intend to ignore + morkRow* row = NextRow(ev, &oid, outRowPos); + if ( row ) + { + morkStore* store = row->GetRowSpaceStore(ev); + if ( store ) + outRow = row->AcquireRowHandle(ev, store); + } + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::PrevRow( // get row cells from table for cells already in row + nsIMdbEnv* mev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) +{ + nsresult outErr = NS_OK; + nsIMdbRow* outRow = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + + mdbOid oid; // place to put oid we intend to ignore + morkRow* row = PrevRow(ev, &oid, outRowPos); + if ( row ) + { + morkStore* store = row->GetRowSpaceStore(ev); + if ( store ) + outRow = row->AcquireRowHandle(ev, store); + } + outErr = ev->AsErr(); + } + if ( acqRow ) + *acqRow = outRow; + return outErr; +} + +// } ----- end row iteration methods ----- + + +// { ----- begin duplicate row removal methods ----- +NS_IMETHODIMP +morkTableRowCursor::CanHaveDupRowMembers(nsIMdbEnv* mev, // cursor might hold dups? + mdb_bool* outCanHaveDups) +{ + nsresult outErr = NS_OK; + mdb_bool canHaveDups = mdbBool_kFalse; + + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + canHaveDups = CanHaveDupRowMembers(ev); + outErr = ev->AsErr(); + } + if ( outCanHaveDups ) + *outCanHaveDups = canHaveDups; + return outErr; +} + +NS_IMETHODIMP +morkTableRowCursor::MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* mev, // context + nsIMdbTableRowCursor** acqCursor) // acquire clone with no dups + // Note that MakeUniqueCursor() is never necessary for a cursor which was + // created by table method nsIMdbTable::GetTableRowCursor(), because a table + // never contains the same row as a member more than once. However, a cursor + // created by table method nsIMdbTable::FindRowMatches() might contain the + // same row more than once, because the same row can generate a hit by more + // than one column with a matching string prefix. Note this method can + // return the very same cursor instance with just an incremented refcount, + // when the original cursor could not contain any duplicate rows (calling + // CanHaveDupRowMembers() shows this case on a false return). Otherwise + // this method returns a different cursor instance. Callers should not use + // this MakeUniqueCursor() method lightly, because it tends to defeat the + // purpose of lazy programming techniques, since it can force creation of + // an explicit row collection in a new cursor's representation, in order to + // inspect the row membership and remove any duplicates; this can have big + // impact if a collection holds tens of thousands of rows or more, when + // the original cursor with dups simply referenced rows indirectly by row + // position ranges, without using an explicit row set representation. + // Callers are encouraged to use nsIMdbCursor::GetCount() to determine + // whether the row collection is very large (tens of thousands), and to + // delay calling MakeUniqueCursor() when possible, until a user interface + // element actually demands the creation of an explicit set representation. +{ + nsresult outErr = NS_OK; + nsIMdbTableRowCursor* outCursor = 0; + + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + AddRef(); + outCursor = this; + + outErr = ev->AsErr(); + } + if ( acqCursor ) + *acqCursor = outCursor; + return outErr; +} +// } ----- end duplicate row removal methods ----- + +// } ===== end nsIMdbTableRowCursor methods ===== + + +/*static*/ void +morkTableRowCursor::NonTableRowCursorTypeError(morkEnv* ev) +{ + ev->NewError("non morkTableRowCursor"); +} + + +mdb_pos +morkTableRowCursor::NextRowOid(morkEnv* ev, mdbOid* outOid) +{ + mdb_pos outPos = -1; + (void) this->NextRow(ev, outOid, &outPos); + return outPos; +} + +mdb_pos +morkTableRowCursor::PrevRowOid(morkEnv* ev, mdbOid* outOid) +{ + mdb_pos outPos = -1; + (void) this->PrevRow(ev, outOid, &outPos); + return outPos; +} + +mork_bool +morkTableRowCursor::CanHaveDupRowMembers(morkEnv* ev) +{ + return morkBool_kFalse; // false default is correct +} + +mork_count +morkTableRowCursor::GetMemberCount(morkEnv* ev) +{ + morkTable* table = mTableRowCursor_Table; + if ( table ) + return table->mTable_RowArray.mArray_Fill; + else + return 0; +} + +morkRow* +morkTableRowCursor::PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos) +{ + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if ( table ) + { + if ( table->IsOpenNode() ) + { + morkArray* array = &table->mTable_RowArray; + pos = mCursor_Pos - 1; + + if ( pos >= 0 && pos < (mork_pos)(array->mArray_Fill) ) + { + mCursor_Pos = pos; // update for next time + morkRow* row = (morkRow*) array->At(pos); + if ( row ) + { + if ( row->IsRow() ) + { + outRow = row; + *outOid = row->mRow_Oid; + } + else + row->NonRowTypeError(ev); + } + else + ev->NilPointerError(); + } + else + { + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + } + } + else + table->NonOpenNodeError(ev); + } + else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +morkRow* +morkTableRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos) +{ + morkRow* outRow = 0; + mork_pos pos = -1; + + morkTable* table = mTableRowCursor_Table; + if ( table ) + { + if ( table->IsOpenNode() ) + { + morkArray* array = &table->mTable_RowArray; + pos = mCursor_Pos; + if ( pos < 0 ) + pos = 0; + else + ++pos; + + if ( pos < (mork_pos)(array->mArray_Fill) ) + { + mCursor_Pos = pos; // update for next time + morkRow* row = (morkRow*) array->At(pos); + if ( row ) + { + if ( row->IsRow() ) + { + outRow = row; + *outOid = row->mRow_Oid; + } + else + row->NonRowTypeError(ev); + } + else + ev->NilPointerError(); + } + else + { + outOid->mOid_Scope = 0; + outOid->mOid_Id = morkId_kMinusOne; + } + } + else + table->NonOpenNodeError(ev); + } + else + ev->NilPointerError(); + + *outPos = pos; + return outRow; +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkTableRowCursor.h b/db/mork/src/morkTableRowCursor.h new file mode 100644 index 0000000000..edd98947b7 --- /dev/null +++ b/db/mork/src/morkTableRowCursor.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKTABLEROWCURSOR_ +#define _MORKTABLEROWCURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinTableRowCursor; +#define morkDerived_kTableRowCursor /*i*/ 0x7243 /* ascii 'rC' */ + +class morkTableRowCursor : public morkCursor, public nsIMdbTableRowCursor { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + +public: // state is public because the entire Mork system is private + morkTable* mTableRowCursor_Table; // weak ref to table + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseTableRowCursor() + +protected: + virtual ~morkTableRowCursor(); // assert that close executed earlier + +public: // morkTableRowCursor construction & destruction + morkTableRowCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos); + void CloseTableRowCursor(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkTableRowCursor(const morkTableRowCursor& other); + morkTableRowCursor& operator=(const morkTableRowCursor& other); + +public: + NS_DECL_ISUPPORTS_INHERITED + + // { ----- begin attribute methods ----- + NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly + NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly + + NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable + NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override; + + NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override; + NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) override; + + // } ----- end attribute methods ----- + NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) override; + // } ----- end attribute methods ----- + + // { ----- begin duplicate row removal methods ----- + NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups? + mdb_bool* outCanHaveDups) override; + + NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows + nsIMdbEnv* ev, // context + nsIMdbTableRowCursor** acqCursor) override; // acquire clone with no dups + // } ----- end duplicate row removal methods ----- + + // { ----- begin oid iteration methods ----- + NS_IMETHOD NextRowOid( // get row id of next row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) override; // zero-based position of the row in table + NS_IMETHOD PrevRowOid( // get row id of previous row in the table + nsIMdbEnv* ev, // context + mdbOid* outOid, // out row oid + mdb_pos* outRowPos) override; // zero-based position of the row in table + // } ----- end oid iteration methods ----- + + // { ----- begin row iteration methods ----- + NS_IMETHOD NextRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire next row in table + mdb_pos* outRowPos) override; // zero-based position of the row in table + NS_IMETHOD PrevRow( // get row cells from table for cells already in row + nsIMdbEnv* ev, // context + nsIMdbRow** acqRow, // acquire previous row in table + mdb_pos* outRowPos) override; // zero-based position of the row in table + // } ----- end row iteration methods ----- + + +public: // dynamic type identification + mork_bool IsTableRowCursor() const + { return IsNode() && mNode_Derived == morkDerived_kTableRowCursor; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonTableRowCursorTypeError(morkEnv* ev); + +public: // oid only iteration + mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + mdb_pos PrevRowOid(morkEnv* ev, mdbOid* outOid); + +public: // other table row cursor methods + + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + virtual morkRow* PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakTableRowCursor(morkTableRowCursor* me, + morkEnv* ev, morkTableRowCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongTableRowCursor(morkTableRowCursor* me, + morkEnv* ev, morkTableRowCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/db/mork/src/morkThumb.cpp b/db/mork/src/morkThumb.cpp new file mode 100644 index 0000000000..ed88ea971f --- /dev/null +++ b/db/mork/src/morkThumb.cpp @@ -0,0 +1,523 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKTHUMB_ +#include "morkThumb.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKWRITER_ +#include "morkWriter.h" +#endif + +#ifndef _MORKPARSER_ +#include "morkParser.h" +#endif + +#ifndef _MORKBUILDER_ +#include "morkBuilder.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkThumb::CloseMorkNode(morkEnv* ev) // CloseThumb() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseThumb(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkThumb::~morkThumb() // assert CloseThumb() executed earlier +{ + CloseMorkNode(mMorkEnv); + MORK_ASSERT(mThumb_Magic==0); + MORK_ASSERT(mThumb_Store==0); + MORK_ASSERT(mThumb_File==0); +} + +/*public non-poly*/ +morkThumb::morkThumb(morkEnv* ev, + const morkUsage& inUsage, nsIMdbHeap* ioHeap, + nsIMdbHeap* ioSlotHeap, mork_magic inMagic) +: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0) +, mThumb_Magic( 0 ) +, mThumb_Total( 0 ) +, mThumb_Current( 0 ) + +, mThumb_Done( morkBool_kFalse ) +, mThumb_Broken( morkBool_kFalse ) +, mThumb_Seed( 0 ) + +, mThumb_Store( 0 ) +, mThumb_File( 0 ) +, mThumb_Writer( 0 ) +, mThumb_Builder( 0 ) +, mThumb_SourcePort( 0 ) + +, mThumb_DoCollect( morkBool_kFalse ) +{ + if ( ev->Good() ) + { + if ( ioSlotHeap ) + { + mThumb_Magic = inMagic; + mNode_Derived = morkDerived_kThumb; + } + else + ev->NilPointerError(); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(morkThumb, morkObject, nsIMdbThumb) + +/*public non-poly*/ void +morkThumb::CloseThumb(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + mThumb_Magic = 0; + if ( mThumb_Builder && mThumb_Store ) + mThumb_Store->ForgetBuilder(ev); + morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mThumb_Builder); + + morkWriter::SlotStrongWriter((morkWriter*) 0, ev, &mThumb_Writer); + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mThumb_File); + morkStore::SlotStrongStore((morkStore*) 0, ev, &mThumb_Store); + morkStore::SlotStrongPort((morkPort*) 0, ev, &mThumb_SourcePort); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +// { ===== begin nsIMdbThumb methods ===== +NS_IMETHODIMP +morkThumb::GetProgress(nsIMdbEnv* mev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + GetProgress(ev, outTotal, outCurrent, outDone, outBroken); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkThumb::DoMore(nsIMdbEnv* mev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + DoMore(ev, outTotal, outCurrent, outDone, outBroken); + outErr = ev->AsErr(); + } + return outErr; +} + +NS_IMETHODIMP +morkThumb::CancelAndBreakThumb(nsIMdbEnv* mev) +{ + nsresult outErr = NS_OK; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + mThumb_Done = morkBool_kTrue; + mThumb_Broken = morkBool_kTrue; + CloseMorkNode(ev); // should I close this here? + outErr = ev->AsErr(); + } + return outErr; +} +// } ===== end nsIMdbThumb methods ===== + +/*static*/ void morkThumb::NonThumbTypeError(morkEnv* ev) +{ + ev->NewError("non morkThumb"); +} + +/*static*/ void morkThumb::UnsupportedThumbMagicError(morkEnv* ev) +{ + ev->NewError("unsupported mThumb_Magic"); +} + +/*static*/ void morkThumb::NilThumbStoreError(morkEnv* ev) +{ + ev->NewError("nil mThumb_Store"); +} + +/*static*/ void morkThumb::NilThumbFileError(morkEnv* ev) +{ + ev->NewError("nil mThumb_File"); +} + +/*static*/ void morkThumb::NilThumbWriterError(morkEnv* ev) +{ + ev->NewError("nil mThumb_Writer"); +} + +/*static*/ void morkThumb::NilThumbBuilderError(morkEnv* ev) +{ + ev->NewError("nil mThumb_Builder"); +} + +/*static*/ void morkThumb::NilThumbSourcePortError(morkEnv* ev) +{ + ev->NewError("nil mThumb_SourcePort"); +} + +/*static*/ morkThumb* +morkThumb::Make_OpenFileStore(morkEnv* ev, nsIMdbHeap* ioHeap, + morkStore* ioStore) +{ + morkThumb* outThumb = 0; + if ( ioHeap && ioStore ) + { + nsIMdbFile* file = ioStore->mStore_File; + if ( file ) + { + mork_pos fileEof = 0; + file->Eof(ev->AsMdbEnv(), &fileEof); + if ( ev->Good() ) + { + outThumb = new(*ioHeap, ev) + morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap, + morkThumb_kMagic_OpenFileStore); + + if ( outThumb ) + { + morkBuilder* builder = ioStore->LazyGetBuilder(ev); + if ( builder ) + { + outThumb->mThumb_Total = (mork_count) fileEof; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + morkBuilder::SlotStrongBuilder(builder, ev, + &outThumb->mThumb_Builder); + } + } + } + } + else + ioStore->NilStoreFileError(ev); + } + else + ev->NilPointerError(); + + return outThumb; +} + + +/*static*/ morkThumb* +morkThumb::Make_LargeCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, morkStore* ioStore) +{ + morkThumb* outThumb = 0; + if ( ioHeap && ioStore ) + { + nsIMdbFile* file = ioStore->mStore_File; + if ( file ) + { + outThumb = new(*ioHeap, ev) + morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap, + morkThumb_kMagic_LargeCommit); + + if ( outThumb ) + { + morkWriter* writer = new(*ioHeap, ev) + morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap); + if ( writer ) + { + writer->mWriter_CommitGroupIdentity = + ++ioStore->mStore_CommitGroupIdentity; + writer->mWriter_NeedDirtyAll = morkBool_kFalse; + outThumb->mThumb_DoCollect = morkBool_kFalse; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + + nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File); + + outThumb->mThumb_Writer = writer; // pass writer ownership to thumb + } + } + } + else + ioStore->NilStoreFileError(ev); + } + else + ev->NilPointerError(); + + return outThumb; +} + +/*static*/ morkThumb* +morkThumb::Make_CompressCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect) +{ + morkThumb* outThumb = 0; + if ( ioHeap && ioStore ) + { + nsIMdbFile* file = ioStore->mStore_File; + if ( file ) + { + outThumb = new(*ioHeap, ev) + morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap, + morkThumb_kMagic_CompressCommit); + + if ( outThumb ) + { + morkWriter* writer = new(*ioHeap, ev) + morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap); + if ( writer ) + { + writer->mWriter_NeedDirtyAll = morkBool_kTrue; + outThumb->mThumb_DoCollect = inDoCollect; + morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store); + nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File); + outThumb->mThumb_Writer = writer; // pass writer ownership to thumb + + // cope with fact that parsed transaction groups are going away: + ioStore->mStore_FirstCommitGroupPos = 0; + ioStore->mStore_SecondCommitGroupPos = 0; + } + } + } + else + ioStore->NilStoreFileError(ev); + } + else + ev->NilPointerError(); + + return outThumb; +} + +// { ===== begin non-poly methods imitating nsIMdbThumb ===== +void morkThumb::GetProgress(morkEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) +{ + MORK_USED_1(ev); + if ( outTotal ) + *outTotal = mThumb_Total; + if ( outCurrent ) + *outCurrent = mThumb_Current; + if ( outDone ) + *outDone = mThumb_Done; + if ( outBroken ) + *outBroken = mThumb_Broken; +} + +void morkThumb::DoMore(morkEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) +{ + if ( !mThumb_Done && !mThumb_Broken ) + { + switch ( mThumb_Magic ) + { + case morkThumb_kMagic_OpenFilePort: // 1 /* factory method */ + this->DoMore_OpenFilePort(ev); break; + + case morkThumb_kMagic_OpenFileStore: // 2 /* factory method */ + this->DoMore_OpenFileStore(ev); break; + + case morkThumb_kMagic_ExportToFormat: // 3 /* port method */ + this->DoMore_ExportToFormat(ev); break; + + case morkThumb_kMagic_ImportContent: // 4 /* store method */ + this->DoMore_ImportContent(ev); break; + + case morkThumb_kMagic_LargeCommit: // 5 /* store method */ + this->DoMore_LargeCommit(ev); break; + + case morkThumb_kMagic_SessionCommit: // 6 /* store method */ + this->DoMore_SessionCommit(ev); break; + + case morkThumb_kMagic_CompressCommit: // 7 /* store method */ + this->DoMore_CompressCommit(ev); break; + + case morkThumb_kMagic_SearchManyColumns: // 8 /* table method */ + this->DoMore_SearchManyColumns(ev); break; + + case morkThumb_kMagic_NewSortColumn: // 9 /* table metho) */ + this->DoMore_NewSortColumn(ev); break; + + case morkThumb_kMagic_NewSortColumnWithCompare: // 10 /* table method */ + this->DoMore_NewSortColumnWithCompare(ev); break; + + case morkThumb_kMagic_CloneSortColumn: // 11 /* table method */ + this->DoMore_CloneSortColumn(ev); break; + + case morkThumb_kMagic_AddIndex: // 12 /* table method */ + this->DoMore_AddIndex(ev); break; + + case morkThumb_kMagic_CutIndex: // 13 /* table method */ + this->DoMore_CutIndex(ev); break; + + default: + this->UnsupportedThumbMagicError(ev); + break; + } + } + if ( outTotal ) + *outTotal = mThumb_Total; + if ( outCurrent ) + *outCurrent = mThumb_Current; + if ( outDone ) + *outDone = mThumb_Done; + if ( outBroken ) + *outBroken = mThumb_Broken; +} + +void morkThumb::CancelAndBreakThumb(morkEnv* ev) +{ + MORK_USED_1(ev); + mThumb_Broken = morkBool_kTrue; +} + +// } ===== end non-poly methods imitating nsIMdbThumb ===== + +morkStore* +morkThumb::ThumbToOpenStore(morkEnv* ev) +// for orkinFactory::ThumbToOpenStore() after OpenFileStore() +{ + MORK_USED_1(ev); + return mThumb_Store; +} + +void morkThumb::DoMore_OpenFilePort(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_OpenFileStore(morkEnv* ev) +{ + morkBuilder* builder = mThumb_Builder; + if ( builder ) + { + mork_pos pos = 0; + builder->ParseMore(ev, &pos, &mThumb_Done, &mThumb_Broken); + // mThumb_Total = builder->mBuilder_TotalCount; + // mThumb_Current = builder->mBuilder_DoneCount; + mThumb_Current = (mork_count) pos; + } + else + { + this->NilThumbBuilderError(ev); + mThumb_Broken = morkBool_kTrue; + mThumb_Done = morkBool_kTrue; + } +} + +void morkThumb::DoMore_ExportToFormat(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_ImportContent(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_LargeCommit(morkEnv* ev) +{ + this->DoMore_Commit(ev); +} + +void morkThumb::DoMore_SessionCommit(morkEnv* ev) +{ + this->DoMore_Commit(ev); +} + +void morkThumb::DoMore_Commit(morkEnv* ev) +{ + morkWriter* writer = mThumb_Writer; + if ( writer ) + { + writer->WriteMore(ev); + mThumb_Total = writer->mWriter_TotalCount; + mThumb_Current = writer->mWriter_DoneCount; + mThumb_Done = ( ev->Bad() || writer->IsWritingDone() ); + mThumb_Broken = ev->Bad(); + } + else + { + this->NilThumbWriterError(ev); + mThumb_Broken = morkBool_kTrue; + mThumb_Done = morkBool_kTrue; + } +} + +void morkThumb::DoMore_CompressCommit(morkEnv* ev) +{ + this->DoMore_Commit(ev); +} + +void morkThumb::DoMore_SearchManyColumns(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_NewSortColumn(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_NewSortColumnWithCompare(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_CloneSortColumn(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_AddIndex(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + +void morkThumb::DoMore_CutIndex(morkEnv* ev) +{ + this->UnsupportedThumbMagicError(ev); +} + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkThumb.h b/db/mork/src/morkThumb.h new file mode 100644 index 0000000000..f17aee9a55 --- /dev/null +++ b/db/mork/src/morkThumb.h @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKTHUMB_ +#define _MORKTHUMB_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKOBJECT_ +#include "morkObject.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +#define morkThumb_kMagic_OpenFilePort 1 /* factory method */ +#define morkThumb_kMagic_OpenFileStore 2 /* factory method */ +#define morkThumb_kMagic_ExportToFormat 3 /* port method */ +#define morkThumb_kMagic_ImportContent 4 /* store method */ +#define morkThumb_kMagic_LargeCommit 5 /* store method */ +#define morkThumb_kMagic_SessionCommit 6 /* store method */ +#define morkThumb_kMagic_CompressCommit 7 /* store method */ +#define morkThumb_kMagic_SearchManyColumns 8 /* table method */ +#define morkThumb_kMagic_NewSortColumn 9 /* table metho) */ +#define morkThumb_kMagic_NewSortColumnWithCompare 10 /* table method */ +#define morkThumb_kMagic_CloneSortColumn 11 /* table method */ +#define morkThumb_kMagic_AddIndex 12 /* table method */ +#define morkThumb_kMagic_CutIndex 13 /* table method */ + +#define morkDerived_kThumb /*i*/ 0x5468 /* ascii 'Th' */ + +/*| morkThumb: +|*/ +class morkThumb : public morkObject, public nsIMdbThumb { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // mork_color mBead_Color; // ID for this bead + // morkHandle* mObject_Handle; // weak ref to handle for this object + +public: // state is public because the entire Mork system is private + NS_DECL_ISUPPORTS_INHERITED + +// { ===== begin nsIMdbThumb methods ===== + NS_IMETHOD GetProgress(nsIMdbEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) override; + + NS_IMETHOD DoMore(nsIMdbEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) override; + + NS_IMETHOD CancelAndBreakThumb(nsIMdbEnv* ev) override; +// } ===== end nsIMdbThumb methods ===== + + // might as well include all the return values here: + + mork_magic mThumb_Magic; // magic sig different in each thumb type + mork_count mThumb_Total; + mork_count mThumb_Current; + + mork_bool mThumb_Done; + mork_bool mThumb_Broken; + mork_u2 mThumb_Seed; // optional seed for u4 alignment padding + + morkStore* mThumb_Store; // weak ref to created store + nsIMdbFile* mThumb_File; // strong ref to file (store, import, export) + morkWriter* mThumb_Writer; // strong ref to writer (for commit) + morkBuilder* mThumb_Builder; // strong ref to builder (for store open) + morkPort* mThumb_SourcePort; // strong ref to port for import + + mork_bool mThumb_DoCollect; // influence whether a collect happens + mork_bool mThumb_Pad[ 3 ]; // padding for u4 alignment + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseThumb() only if open + +public: // morkThumb construction & destruction + morkThumb(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_magic inMagic); + void CloseThumb(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkThumb(const morkThumb& other); + morkThumb& operator=(const morkThumb& other); + virtual ~morkThumb(); // assert that CloseThumb() executed earlier + +public: // dynamic type identification + mork_bool IsThumb() const + { return IsNode() && mNode_Derived == morkDerived_kThumb; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonThumbTypeError(morkEnv* ev); + static void UnsupportedThumbMagicError(morkEnv* ev); + + static void NilThumbStoreError(morkEnv* ev); + static void NilThumbFileError(morkEnv* ev); + static void NilThumbWriterError(morkEnv* ev); + static void NilThumbBuilderError(morkEnv* ev); + static void NilThumbSourcePortError(morkEnv* ev); + +public: // 'do more' methods + + void DoMore_OpenFilePort(morkEnv* ev); + void DoMore_OpenFileStore(morkEnv* ev); + void DoMore_ExportToFormat(morkEnv* ev); + void DoMore_ImportContent(morkEnv* ev); + void DoMore_LargeCommit(morkEnv* ev); + void DoMore_SessionCommit(morkEnv* ev); + void DoMore_CompressCommit(morkEnv* ev); + void DoMore_Commit(morkEnv* ev); + void DoMore_SearchManyColumns(morkEnv* ev); + void DoMore_NewSortColumn(morkEnv* ev); + void DoMore_NewSortColumnWithCompare(morkEnv* ev); + void DoMore_CloneSortColumn(morkEnv* ev); + void DoMore_AddIndex(morkEnv* ev); + void DoMore_CutIndex(morkEnv* ev); + +public: // other thumb methods + + morkStore* ThumbToOpenStore(morkEnv* ev); + // for orkinFactory::ThumbToOpenStore() after OpenFileStore() + +public: // assorted thumb constructors + + static morkThumb* Make_OpenFileStore(morkEnv* ev, + nsIMdbHeap* ioHeap, morkStore* ioStore); + + static morkThumb* Make_CompressCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect); + + static morkThumb* Make_LargeCommit(morkEnv* ev, + nsIMdbHeap* ioHeap, morkStore* ioStore); + +// { ===== begin non-poly methods imitating nsIMdbThumb ===== + void GetProgress(morkEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken); + + void DoMore(morkEnv* ev, mdb_count* outTotal, + mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken); + + void CancelAndBreakThumb(morkEnv* ev); +// } ===== end non-poly methods imitating nsIMdbThumb ===== + + + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakThumb(morkThumb* me, + morkEnv* ev, morkThumb** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongThumb(morkThumb* me, + morkEnv* ev, morkThumb** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTHUMB_ */ diff --git a/db/mork/src/morkUniqRowCursor.h b/db/mork/src/morkUniqRowCursor.h new file mode 100644 index 0000000000..44eac94712 --- /dev/null +++ b/db/mork/src/morkUniqRowCursor.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKUNIQROWCURSOR_ +#define _MORKUNIQROWCURSOR_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKCURSOR_ +#include "morkCursor.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +class orkinTableRowCursor; +// #define morkDerived_kUniqRowCursor /*i*/ 0x7352 /* ascii 'sR' */ + +class morkUniqRowCursor : public morkTableRowCursor { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + + // morkFactory* mObject_Factory; // weak ref to suite factory + + // mork_seed mCursor_Seed; + // mork_pos mCursor_Pos; + // mork_bool mCursor_DoFailOnSeedOutOfSync; + // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment + + // morkTable* mTableRowCursor_Table; // weak ref to table + +public: // state is public because the entire Mork system is private + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseUniqRowCursor() + virtual ~morkUniqRowCursor(); // assert that close executed earlier + +public: // morkUniqRowCursor construction & destruction + morkUniqRowCursor(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos); + void CloseUniqRowCursor(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkUniqRowCursor(const morkUniqRowCursor& other); + morkUniqRowCursor& operator=(const morkUniqRowCursor& other); + +public: // dynamic type identification + // mork_bool IsUniqRowCursor() const + // { return IsNode() && mNode_Derived == morkDerived_kUniqRowCursor; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonUniqRowCursorTypeError(morkEnv* ev); + +public: // other search row cursor methods + + virtual mork_bool CanHaveDupRowMembers(morkEnv* ev); + virtual mork_count GetMemberCount(morkEnv* ev); + + virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev); + + // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid); + virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakUniqRowCursor(morkUniqRowCursor* me, + morkEnv* ev, morkUniqRowCursor** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongUniqRowCursor(morkUniqRowCursor* me, + morkEnv* ev, morkUniqRowCursor** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKUNIQROWCURSOR_ */ diff --git a/db/mork/src/morkWriter.cpp b/db/mork/src/morkWriter.cpp new file mode 100644 index 0000000000..98ca5457f0 --- /dev/null +++ b/db/mork/src/morkWriter.cpp @@ -0,0 +1,2206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKBLOB_ +#include "morkBlob.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKARRAY_ +#include "morkWriter.h" +#endif + +// #ifndef _MORKFILE_ +// #include "morkFile.h" +// #endif + +#ifndef _MORKSTREAM_ +#include "morkStream.h" +#endif + +#ifndef _MORKSTORE_ +#include "morkStore.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKATOMMAP_ +#include "morkAtomMap.h" +#endif + +#ifndef _MORKROW_ +#include "morkRow.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKCELL_ +#include "morkCell.h" +#endif + +#ifndef _MORKATOM_ +#include "morkAtom.h" +#endif + +#ifndef _MORKCH_ +#include "morkCh.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkWriter::CloseMorkNode(morkEnv* ev) // CloseTable() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseWriter(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkWriter::~morkWriter() // assert CloseTable() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); + MORK_ASSERT(mWriter_Store==0); +} + +/*public non-poly*/ +morkWriter::morkWriter(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile, + nsIMdbHeap* ioSlotHeap) +: morkNode(ev, inUsage, ioHeap) +, mWriter_Store( 0 ) +, mWriter_File( 0 ) +, mWriter_Bud( 0 ) +, mWriter_Stream( 0 ) +, mWriter_SlotHeap( 0 ) + +, mWriter_CommitGroupIdentity( 0 ) // see mStore_CommitGroupIdentity +, mWriter_GroupBufFill( 0 ) + +, mWriter_TotalCount( morkWriter_kCountNumberOfPhases ) +, mWriter_DoneCount( 0 ) + +, mWriter_LineSize( 0 ) +, mWriter_MaxIndent( morkWriter_kMaxIndent ) +, mWriter_MaxLine( morkWriter_kMaxLine ) + +, mWriter_TableForm( 0 ) +, mWriter_TableAtomScope( 'v' ) +, mWriter_TableRowScope( 0 ) +, mWriter_TableKind( 0 ) + +, mWriter_RowForm( 0 ) +, mWriter_RowAtomScope( 0 ) +, mWriter_RowScope( 0 ) + +, mWriter_DictForm( 0 ) +, mWriter_DictAtomScope( 'v' ) + +, mWriter_NeedDirtyAll( morkBool_kFalse ) +, mWriter_Incremental( morkBool_kTrue ) // opposite of mWriter_NeedDirtyAll +, mWriter_DidStartDict( morkBool_kFalse ) +, mWriter_DidEndDict( morkBool_kTrue ) + +, mWriter_SuppressDirtyRowNewline( morkBool_kFalse ) +, mWriter_DidStartGroup( morkBool_kFalse ) +, mWriter_DidEndGroup( morkBool_kTrue ) +, mWriter_Phase( morkWriter_kPhaseNothingDone ) + +, mWriter_BeVerbose( ev->mEnv_BeVerbose ) + +, mWriter_TableRowArrayPos( 0 ) + +// empty constructors for map iterators: +, mWriter_StoreAtomSpacesIter( ) +, mWriter_AtomSpaceAtomAidsIter( ) + +, mWriter_StoreRowSpacesIter( ) +, mWriter_RowSpaceTablesIter( ) +, mWriter_RowSpaceRowsIter( ) +{ + mWriter_GroupBuf[ 0 ] = 0; + + mWriter_SafeNameBuf[ 0 ] = 0; + mWriter_SafeNameBuf[ morkWriter_kMaxColumnNameSize * 2 ] = 0; + mWriter_ColNameBuf[ 0 ] = 0; + mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize ] = 0; + + mdbYarn* y = &mWriter_ColYarn; + y->mYarn_Buf = mWriter_ColNameBuf; // where to put col bytes + y->mYarn_Fill = 0; // set later by writer + y->mYarn_Size = morkWriter_kMaxColumnNameSize; // our buf size + y->mYarn_More = 0; // set later by writer + y->mYarn_Form = 0; // set later by writer + y->mYarn_Grow = 0; // do not allow buffer growth + + y = &mWriter_SafeYarn; + y->mYarn_Buf = mWriter_SafeNameBuf; // where to put col bytes + y->mYarn_Fill = 0; // set later by writer + y->mYarn_Size = morkWriter_kMaxColumnNameSize * 2; // our buf size + y->mYarn_More = 0; // set later by writer + y->mYarn_Form = 0; // set later by writer + y->mYarn_Grow = 0; // do not allow buffer growth + + if ( ev->Good() ) + { + if ( ioSlotHeap && ioFile && ioStore ) + { + morkStore::SlotWeakStore(ioStore, ev, &mWriter_Store); + nsIMdbFile_SlotStrongFile(ioFile, ev, &mWriter_File); + nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mWriter_SlotHeap); + if ( ev->Good() ) + { + mNode_Derived = morkDerived_kWriter; + } + } + else + ev->NilPointerError(); + } +} + + +void +morkWriter::MakeWriterStream(morkEnv* ev) // give writer a suitable stream +{ + mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites + + if ( !mWriter_Stream && ev->Good() ) + { + if ( mWriter_File ) + { + morkStream* stream = 0; + mork_bool frozen = morkBool_kFalse; // need to modify + nsIMdbHeap* heap = mWriter_SlotHeap; + + if ( mWriter_Incremental ) + { + stream = new(*heap, ev) + morkStream(ev, morkUsage::kHeap, heap, mWriter_File, + morkWriter_kStreamBufSize, frozen); + } + else // compress commit + { + nsIMdbFile* bud = 0; + mWriter_File->AcquireBud(ev->AsMdbEnv(), heap, &bud); + if ( bud ) + { + if ( ev->Good() ) + { + mWriter_Bud = bud; + stream = new(*heap, ev) + morkStream(ev, morkUsage::kHeap, heap, bud, + morkWriter_kStreamBufSize, frozen); + } + else + bud->Release(); + } + } + + if ( stream ) + { + if ( ev->Good() ) + mWriter_Stream = stream; + else + stream->CutStrongRef(ev->AsMdbEnv()); + } + } + else + this->NilWriterFileError(ev); + } +} + +/*public non-poly*/ void +morkWriter::CloseWriter(morkEnv* ev) // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + { + morkStore::SlotWeakStore((morkStore*) 0, ev, &mWriter_Store); + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_File); + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud); + morkStream::SlotStrongStream((morkStream*) 0, ev, &mWriter_Stream); + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mWriter_SlotHeap); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkWriter::NonWriterTypeError(morkEnv* ev) +{ + ev->NewError("non morkWriter"); +} + +/*static*/ void +morkWriter::NilWriterStoreError(morkEnv* ev) +{ + ev->NewError("nil mWriter_Store"); +} + +/*static*/ void +morkWriter::NilWriterBudError(morkEnv* ev) +{ + ev->NewError("nil mWriter_Bud"); +} + +/*static*/ void +morkWriter::NilWriterFileError(morkEnv* ev) +{ + ev->NewError("nil mWriter_File"); +} + +/*static*/ void +morkWriter::NilWriterStreamError(morkEnv* ev) +{ + ev->NewError("nil mWriter_Stream"); +} + +/*static*/ void +morkWriter::UnsupportedPhaseError(morkEnv* ev) +{ + ev->NewError("unsupported mWriter_Phase"); +} + +mork_bool +morkWriter::WriteMore(morkEnv* ev) // call until IsWritingDone() is true +{ + if ( this->IsOpenNode() ) + { + if ( this->IsWriter() ) + { + if ( !mWriter_Stream ) + this->MakeWriterStream(ev); + + if ( mWriter_Stream ) + { + if ( ev->Bad() ) + { + ev->NewWarning("writing stops on error"); + mWriter_Phase = morkWriter_kPhaseWritingDone; + } + switch( mWriter_Phase ) + { + case morkWriter_kPhaseNothingDone: + OnNothingDone(ev); break; + + case morkWriter_kPhaseDirtyAllDone: + OnDirtyAllDone(ev); break; + + case morkWriter_kPhasePutHeaderDone: + OnPutHeaderDone(ev); break; + + case morkWriter_kPhaseRenumberAllDone: + OnRenumberAllDone(ev); break; + + case morkWriter_kPhaseStoreAtomSpaces: + OnStoreAtomSpaces(ev); break; + + case morkWriter_kPhaseAtomSpaceAtomAids: + OnAtomSpaceAtomAids(ev); break; + + case morkWriter_kPhaseStoreRowSpacesTables: + OnStoreRowSpacesTables(ev); break; + + case morkWriter_kPhaseRowSpaceTables: + OnRowSpaceTables(ev); break; + + case morkWriter_kPhaseTableRowArray: + OnTableRowArray(ev); break; + + case morkWriter_kPhaseStoreRowSpacesRows: + OnStoreRowSpacesRows(ev); break; + + case morkWriter_kPhaseRowSpaceRows: + OnRowSpaceRows(ev); break; + + case morkWriter_kPhaseContentDone: + OnContentDone(ev); break; + + case morkWriter_kPhaseWritingDone: + OnWritingDone(ev); break; + + default: + this->UnsupportedPhaseError(ev); + } + } + else + this->NilWriterStreamError(ev); + } + else + this->NonWriterTypeError(ev); + } + else + this->NonOpenNodeError(ev); + + return ev->Good(); +} + +static const char morkWriter_kHexDigits[] = "0123456789ABCDEF"; + +mork_size +morkWriter::WriteYarn(morkEnv* ev, const mdbYarn* inYarn) + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire yarn's size, since only part goes on a last line). +{ + + + mork_size outSize = 0; + mork_size lineSize = mWriter_LineSize; + morkStream* stream = mWriter_Stream; + + const mork_u1* b = (const mork_u1*) inYarn->mYarn_Buf; + if ( b ) + { + int c; + mork_fill fill = inYarn->mYarn_Fill; + + const mork_u1* end = b + fill; + while ( b < end && ev->Good() ) + { + if ( lineSize + outSize >= mWriter_MaxLine ) // continue line? + { + stream->PutByteThenNewline(ev, '\\'); + mWriter_LineSize = lineSize = outSize = 0; + } + + c = *b++; // next byte to print + if ( morkCh_IsValue(c) ) + { + stream->Putc(ev, c); + ++outSize; // c + } + else if ( c == ')' || c == '$' || c == '\\' ) + { + stream->Putc(ev, '\\'); + stream->Putc(ev, c); + outSize += 2; // '\' c + } + else + { + outSize += 3; // '$' hex hex + stream->Putc(ev, '$'); + stream->Putc(ev, morkWriter_kHexDigits[ (c >> 4) & 0x0F ]); + stream->Putc(ev, morkWriter_kHexDigits[ c & 0x0F ]); + } + } + } + mWriter_LineSize += outSize; + + return outSize; +} + +mork_size +morkWriter::WriteAtom(morkEnv* ev, const morkAtom* inAtom) + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire atom's size, since only part goes on a last line). +{ + mork_size outSize = 0; + mdbYarn yarn; // to ref content inside atom + + if ( morkAtom::AliasYarn(inAtom, &yarn) ) + { + if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm ) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + outSize = this->WriteYarn(ev, &yarn); + // mWriter_LineSize += stream->Write(ev, inYarn->mYarn_Buf, outSize); + } + else + inAtom->BadAtomKindError(ev); + + return outSize; +} + +void +morkWriter::WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace) +{ + morkStream* stream = mWriter_Stream; + nsIMdbEnv *mdbev = ev->AsMdbEnv(); + mork_scope scope = ioSpace->SpaceScope(); + if ( scope < 0x80 ) + { + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + stream->PutString(ev, "< <(a="); + stream->Putc(ev, (int) scope); + ++mWriter_LineSize; + stream->PutString(ev, ")> // (f=iso-8859-1)"); + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth); + } + else + ioSpace->NonAsciiSpaceScopeName(ev); + + if ( ev->Good() ) + { + mdbYarn yarn; // to ref content inside atom + char buf[ 64 ]; // buffer for staging the dict alias hex ID + char* idBuf = buf + 1; // where the id always starts + buf[ 0 ] = '('; // we always start with open paren + morkBookAtom* atom = 0; + morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter; + ai->InitAtomAidMapIter(ev, &ioSpace->mAtomSpace_AtomAids); + mork_change* c = 0; + + for ( c = ai->FirstAtom(ev, &atom); c && ev->Good(); + c = ai->NextAtom(ev, &atom) ) + { + if ( atom ) + { + if ( atom->IsAtomDirty() ) + { + atom->SetAtomClean(); // neutralize change + + morkAtom::AliasYarn(atom, &yarn); + mork_size size = ev->TokenAsHex(idBuf, atom->mBookAtom_Id); + + if ( yarn.mYarn_Form != mWriter_DictForm ) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + mork_size pending = yarn.mYarn_Fill + size + + morkWriter_kYarnEscapeSlop + 4; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + mork_size bytesWritten; + stream->Write(mdbev, buf, size+1, &bytesWritten); // + '(' + mWriter_LineSize += bytesWritten; + + pending -= ( size + 1 ); + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth); + stream->Putc(ev, '='); // start alias + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + stream->Putc(ev, ')'); // end alias + ++mWriter_LineSize; + + ++mWriter_DoneCount; + } + } + else + ev->NilPointerError(); + } + ai->CloseMapIter(ev); + } + + if ( ev->Good() ) + { + ioSpace->SetAtomSpaceClean(); + // this->IndentAsNeeded(ev, 0); + // stream->PutByteThenNewline(ev, '>'); // end dict + + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } +} + +/* +(I'm putting the text of this message in file morkWriter.cpp.) + +I'm making a change which should cause rows and tables to go away +when a Mork db is compress committed, when the rows and tables +are no longer needed. Because this is subtle, I'm describing it +here in case misbehavior is ever observed. Otherwise you'll have +almost no hope of fixing a related bug. + +This is done entirely in morkWriter.cpp: morkWriter::DirtyAll(), +which currently marks all rows and tables dirty so they will be +written in a later phase of the commit. My change is to merely +selectively not mark certain rows and tables dirty, when they seem +to be superfluous. + +A row is no longer needed when the mRow_GcUses slot hits zero, and +this is used by the following inline morkRow method: + + mork_bool IsRowUsed() const { return mRow_GcUses != 0; } + +Naturally disaster ensues if mRow_GcUses is ever smaller than right. + +Similarly, we should drop tables when mTable_GcUses hits zero, but +only when a table contains no row members. We consider tables to +self reference (and prevent collection) when they contain content. +Again, disaster ensues if mTable_GcUses is ever smaller than right. + + mork_count GetRowCount() const + { return mTable_RowArray.mArray_Fill; } + + mork_bool IsTableUsed() const + { return (mTable_GcUses != 0 || this->GetRowCount() != 0); } + +Now let's question why the design involves filtering what gets set +to dirty. Why not apply a filter in the later phase when we write +content? Because I'm afraid of missing some subtle interaction in +updating table and row relationships. It seems safer to write a row +or table when it starts out dirty, before morkWriter::DirtyAll() is +called. So this design calls for writing out rows and tables when +they are still clearly used, and additionally, when we have just +been actively writing to them right before this commit. + +Presumably if they are truly useless, they will no longer be dirtied +in later sessions and will get collected during the next compress +commit. So we wait to collect them until they become all dead, and +not just mostly dead. (At which time you can feel free to go through +their pockets looking for loose change.) +*/ + +mork_bool +morkWriter::DirtyAll(morkEnv* ev) + // DirtyAll() visits every store sub-object and marks + // them dirty, including every table, row, cell, and atom. The return + // equals ev->Good(), to show whether any error happened. This method is + // intended for use in the beginning of a "compress commit" which writes + // all store content, whether dirty or not. We dirty everything first so + // that later iterations over content can mark things clean as they are + // written, and organize the process of serialization so that objects are + // written only at need (because of being dirty). Note the method can + // stop early when any error happens, since this will abort any commit. +{ + morkStore* store = mWriter_Store; + if ( store ) + { + store->SetStoreDirty(); + mork_change* c = 0; + + if ( ev->Good() ) + { + morkAtomSpaceMapIter* asi = &mWriter_StoreAtomSpacesIter; + asi->InitAtomSpaceMapIter(ev, &store->mStore_AtomSpaces); + + mork_scope* key = 0; // ignore keys in map + morkAtomSpace* space = 0; // old val node in the map + + for ( c = asi->FirstAtomSpace(ev, key, &space); c && ev->Good(); + c = asi->NextAtomSpace(ev, key, &space) ) + { + if ( space ) + { + if ( space->IsAtomSpace() ) + { + space->SetAtomSpaceDirty(); + morkBookAtom* atom = 0; + morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter; + ai->InitAtomAidMapIter(ev, &space->mAtomSpace_AtomAids); + + for ( c = ai->FirstAtom(ev, &atom); c && ev->Good(); + c = ai->NextAtom(ev, &atom) ) + { + if ( atom ) + { + atom->SetAtomDirty(); + ++mWriter_TotalCount; + } + else + ev->NilPointerError(); + } + + ai->CloseMapIter(ev); + } + else + space->NonAtomSpaceTypeError(ev); + } + else + ev->NilPointerError(); + } + } + + if ( ev->Good() ) + { + morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + mork_scope* key = 0; // ignore keys in map + morkRowSpace* space = 0; // old val node in the map + + for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good(); + c = rsi->NextRowSpace(ev, key, &space) ) + { + if ( space ) + { + if ( space->IsRowSpace() ) + { + space->SetRowSpaceDirty(); + if ( ev->Good() ) + { +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter; +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter* ri = &mWriter_RowSpaceRowsIter; +#endif /*MORK_ENABLE_PROBE_MAPS*/ + ri->InitRowMapIter(ev, &space->mRowSpace_Rows); + + morkRow* row = 0; // old key row in the map + + for ( c = ri->FirstRow(ev, &row); c && ev->Good(); + c = ri->NextRow(ev, &row) ) + { + if ( row && row->IsRow() ) // need to dirty row? + { + if ( row->IsRowUsed() || row->IsRowDirty() ) + { + row->DirtyAllRowContent(ev); + ++mWriter_TotalCount; + } + } + else + row->NonRowTypeWarning(ev); + } + ri->CloseMapIter(ev); + } + + if ( ev->Good() ) + { + morkTableMapIter* ti = &mWriter_RowSpaceTablesIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTable* table = ti->FirstTable(ev); + + for ( ; table && ev->Good(); table = ti->NextTable(ev) ) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* tableKey = 0; // ignore keys in table map + morkTable* table = 0; // old key row in the map + + for ( c = ti->FirstTable(ev, tableKey, &table); c && ev->Good(); + c = ti->NextTable(ev, tableKey, &table) ) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if ( table && table->IsTable() ) // need to dirty table? + { + if ( table->IsTableUsed() || table->IsTableDirty() ) + { + // table->DirtyAllTableContent(ev); + // only necessary to mark table itself dirty: + table->SetTableDirty(); + table->SetTableRewrite(); + ++mWriter_TotalCount; + } + } + else + table->NonTableTypeWarning(ev); + } + ti->CloseMapIter(ev); + } + } + else + space->NonRowSpaceTypeError(ev); + } + else + ev->NilPointerError(); + } + } + } + else + this->NilWriterStoreError(ev); + + return ev->Good(); +} + + +mork_bool +morkWriter::OnNothingDone(morkEnv* ev) +{ + mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites + + if (!mWriter_Store->IsStoreDirty() && !mWriter_NeedDirtyAll) + { + mWriter_Phase = morkWriter_kPhaseWritingDone; + return morkBool_kTrue; + } + + // morkStream* stream = mWriter_Stream; + if ( mWriter_NeedDirtyAll ) + this->DirtyAll(ev); + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseDirtyAllDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::StartGroup(morkEnv* ev) +{ + nsIMdbEnv *mdbev = ev->AsMdbEnv(); + morkStream* stream = mWriter_Stream; + mWriter_DidStartGroup = morkBool_kTrue; + mWriter_DidEndGroup = morkBool_kFalse; + + char buf[ 64 ]; + char* p = buf; + *p++ = '@'; + *p++ = '$'; + *p++ = '$'; + *p++ = '{'; + + mork_token groupID = mWriter_CommitGroupIdentity; + mork_fill idFill = ev->TokenAsHex(p, groupID); + mWriter_GroupBufFill = 0; + // ev->TokenAsHex(mWriter_GroupBuf, groupID); + if ( idFill < morkWriter_kGroupBufSize ) + { + MORK_MEMCPY(mWriter_GroupBuf, p, idFill + 1); + mWriter_GroupBufFill = idFill; + } + else + *mWriter_GroupBuf = 0; + + p += idFill; + *p++ = '{'; + *p++ = '@'; + *p = 0; + + stream->PutLineBreak(ev); + + morkStore* store = mWriter_Store; + if ( store ) // might need to capture commit group position? + { + mork_pos groupPos; + stream->Tell(mdbev, &groupPos); + if ( !store->mStore_FirstCommitGroupPos ) + store->mStore_FirstCommitGroupPos = groupPos; + else if ( !store->mStore_SecondCommitGroupPos ) + store->mStore_SecondCommitGroupPos = groupPos; + } + + mork_size bytesWritten; + stream->Write(mdbev, buf, idFill + 6, &bytesWritten); // '@$${' + idFill + '{@' + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + return ev->Good(); +} + +mork_bool +morkWriter::CommitGroup(morkEnv* ev) +{ + if ( mWriter_DidStartGroup ) + { + nsIMdbEnv *mdbev = ev->AsMdbEnv(); + mork_size bytesWritten; + morkStream* stream = mWriter_Stream; + + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + stream->Putc(ev, '@'); + stream->Putc(ev, '$'); + stream->Putc(ev, '$'); + stream->Putc(ev, '}'); + + mork_fill bufFill = mWriter_GroupBufFill; + if ( bufFill ) + stream->Write(mdbev, mWriter_GroupBuf, bufFill, &bytesWritten); + + stream->Putc(ev, '}'); + stream->Putc(ev, '@'); + stream->PutLineBreak(ev); + + mWriter_LineSize = 0; + } + + mWriter_DidStartGroup = morkBool_kFalse; + mWriter_DidEndGroup = morkBool_kTrue; + + return ev->Good(); +} + +mork_bool +morkWriter::AbortGroup(morkEnv* ev) +{ + if ( mWriter_DidStartGroup ) + { + morkStream* stream = mWriter_Stream; + stream->PutLineBreak(ev); + stream->PutStringThenNewline(ev, "@$$}~~}@"); + mWriter_LineSize = 0; + } + + mWriter_DidStartGroup = morkBool_kFalse; + mWriter_DidEndGroup = morkBool_kTrue; + + return ev->Good(); +} + + +mork_bool +morkWriter::OnDirtyAllDone(morkEnv* ev) +{ + if ( ev->Good() ) + { + nsIMdbEnv *mdbev = ev->AsMdbEnv(); + morkStream* stream = mWriter_Stream; + mork_pos resultPos; + if ( mWriter_NeedDirtyAll ) // compress commit + { + + stream->Seek(mdbev, 0, &resultPos); // beginning of stream + stream->PutStringThenNewline(ev, morkWriter_kFileHeader); + mWriter_LineSize = 0; + } + else // else mWriter_Incremental + { + mork_pos eos = stream->Length(ev); // length is end of stream + if ( ev->Good() ) + { + stream->Seek(mdbev, eos, &resultPos); // goto end of stream + if ( eos < 128 ) // maybe need file header? + { + stream->PutStringThenNewline(ev, morkWriter_kFileHeader); + mWriter_LineSize = 0; + } + this->StartGroup(ev); // begin incremental transaction + } + } + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhasePutHeaderDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnPutHeaderDone(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnPutHeaderDone()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + morkStore* store = mWriter_Store; + if ( store ) + store->RenumberAllCollectableContent(ev); + else + this->NilWriterStoreError(ev); + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseRenumberAllDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnRenumberAllDone(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRenumberAllDone()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreAtomSpaces; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnStoreAtomSpaces(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreAtomSpaces()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + { + morkStore* store = mWriter_Store; + if ( store ) + { + morkAtomSpace* space = store->LazyGetGroundColumnSpace(ev); + if ( space && space->IsAtomSpaceDirty() ) + { + // stream->PutStringThenNewline(ev, "// ground column space dict:"); + + if ( mWriter_LineSize ) + { + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + } + this->WriteAtomSpaceAsDict(ev, space); + space->SetAtomSpaceClean(); + } + } + else + this->NilWriterStoreError(ev); + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnAtomSpaceAtomAids(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnAtomSpaceAtomAids()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +void +morkWriter::WriteAllStoreTables(morkEnv* ev) +{ + morkStore* store = mWriter_Store; + if ( store && ev->Good() ) + { + morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter; + rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces); + + mork_scope* key = 0; // ignore keys in map + morkRowSpace* space = 0; // old val node in the map + mork_change* c = 0; + + for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good(); + c = rsi->NextRowSpace(ev, key, &space) ) + { + if ( space ) + { + if ( space->IsRowSpace() ) + { + space->SetRowSpaceClean(); + if ( ev->Good() ) + { + morkTableMapIter* ti = &mWriter_RowSpaceTablesIter; + ti->InitTableMapIter(ev, &space->mRowSpace_Tables); + +#ifdef MORK_BEAD_OVER_NODE_MAPS + morkTable* table = ti->FirstTable(ev); + + for ( ; table && ev->Good(); table = ti->NextTable(ev) ) +#else /*MORK_BEAD_OVER_NODE_MAPS*/ + mork_tid* key2 = 0; // ignore keys in table map + morkTable* table = 0; // old key row in the map + + for ( c = ti->FirstTable(ev, key2, &table); c && ev->Good(); + c = ti->NextTable(ev, key2, &table) ) +#endif /*MORK_BEAD_OVER_NODE_MAPS*/ + { + if ( table && table->IsTable() ) + { + if ( table->IsTableDirty() ) + { + mWriter_BeVerbose = + ( ev->mEnv_BeVerbose || table->IsTableVerbose() ); + + if ( this->PutTableDict(ev, table) ) + this->PutTable(ev, table); + + table->SetTableClean(ev); + mWriter_BeVerbose = ev->mEnv_BeVerbose; + } + } + else + table->NonTableTypeWarning(ev); + } + ti->CloseMapIter(ev); + } + if ( ev->Good() ) + { + mWriter_TableRowScope = 0; // ensure no table context now + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter; +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter* ri = &mWriter_RowSpaceRowsIter; +#endif /*MORK_ENABLE_PROBE_MAPS*/ + ri->InitRowMapIter(ev, &space->mRowSpace_Rows); + + morkRow* row = 0; // old row in the map + + for ( c = ri->FirstRow(ev, &row); c && ev->Good(); + c = ri->NextRow(ev, &row) ) + { + if ( row && row->IsRow() ) + { + // later we should also check that table use count is nonzero: + if ( row->IsRowDirty() ) // && row->IsRowUsed() ?? + { + mWriter_BeVerbose = ev->mEnv_BeVerbose; + if ( this->PutRowDict(ev, row) ) + { + if ( ev->Good() && mWriter_DidStartDict ) + { + this->EndDict(ev); + if ( mWriter_LineSize < 32 && ev->Good() ) + mWriter_SuppressDirtyRowNewline = morkBool_kTrue; + } + + if ( ev->Good() ) + this->PutRow(ev, row); + } + mWriter_BeVerbose = ev->mEnv_BeVerbose; + } + } + else + row->NonRowTypeWarning(ev); + } + ri->CloseMapIter(ev); + } + } + else + space->NonRowSpaceTypeError(ev); + } + else + ev->NilPointerError(); + } + } +} + +mork_bool +morkWriter::OnStoreRowSpacesTables(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesTables()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + // later we'll break this up, but today we'll write all in one shot: + this->WriteAllStoreTables(ev); + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnRowSpaceTables(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRowSpaceTables()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnTableRowArray(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnTableRowArray()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnStoreRowSpacesRows(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesRows()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseContentDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnRowSpaceRows(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnRowSpaceRows()"); + mWriter_LineSize = 0; + + if ( mWriter_NeedDirtyAll ) // compress commit + { + } + + if ( ev->Good() ) + mWriter_Phase = morkWriter_kPhaseContentDone; + else + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error + + return ev->Good(); +} + +mork_bool +morkWriter::OnContentDone(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + + // if ( mWriter_NeedDirtyAll ) + // stream->PutStringThenNewline(ev, "// OnContentDone()"); + mWriter_LineSize = 0; + + if ( mWriter_Incremental ) + { + if ( ev->Good() ) + this->CommitGroup(ev); + else + this->AbortGroup(ev); + } + else if ( mWriter_Store && ev->Good() ) + { + // after rewriting everything, there are no transaction groups: + mWriter_Store->mStore_FirstCommitGroupPos = 0; + mWriter_Store->mStore_SecondCommitGroupPos = 0; + } + + stream->Flush(ev->AsMdbEnv()); + nsIMdbFile* bud = mWriter_Bud; + if ( bud ) + { + bud->Flush(ev->AsMdbEnv()); + bud->BecomeTrunk(ev->AsMdbEnv()); + nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud); + } + else if ( !mWriter_Incremental ) // should have a bud? + this->NilWriterBudError(ev); + + mWriter_Phase = morkWriter_kPhaseWritingDone; // stop always + mWriter_DoneCount = mWriter_TotalCount; + + return ev->Good(); +} + +mork_bool +morkWriter::OnWritingDone(morkEnv* ev) +{ + mWriter_DoneCount = mWriter_TotalCount; + ev->NewWarning("writing is done"); + return ev->Good(); +} + +mork_bool +morkWriter::PutTableChange(morkEnv* ev, const morkTableChange* inChange) +{ + nsIMdbEnv *mdbev = ev->AsMdbEnv(); + if ( inChange->IsAddRowTableChange() ) + { + this->PutRow(ev, inChange->mTableChange_Row ); // row alone means add + } + else if ( inChange->IsCutRowTableChange() ) + { + mWriter_Stream->Putc(ev, '-'); // prefix '-' indicates cut row + ++mWriter_LineSize; + this->PutRow(ev, inChange->mTableChange_Row ); + } + else if ( inChange->IsMoveRowTableChange() ) + { + this->PutRow(ev, inChange->mTableChange_Row ); + char buf[ 64 ]; + char* p = buf; + *p++ = '!'; // for moves, position is indicated by prefix '!' + mork_size posSize = ev->TokenAsHex(p, inChange->mTableChange_Pos); + p += posSize; + *p++ = ' '; + mork_size bytesWritten; + mWriter_Stream->Write(mdbev, buf, posSize + 2, &bytesWritten); + mWriter_LineSize += bytesWritten; + } + else + inChange->UnknownChangeError(ev); + + return ev->Good(); +} + +mork_bool +morkWriter::PutTable(morkEnv* ev, morkTable* ioTable) +{ + if ( ev->Good() ) + this->StartTable(ev, ioTable); + + if ( ev->Good() ) + { + if ( ioTable->IsTableRewrite() || mWriter_NeedDirtyAll ) + { + morkArray* array = &ioTable->mTable_RowArray; // vector of rows + mork_fill fill = array->mArray_Fill; // count of rows + morkRow** rows = (morkRow**) array->mArray_Slots; + if ( rows && fill ) + { + morkRow** end = rows + fill; + while ( rows < end && ev->Good() ) + { + morkRow* r = *rows++; // next row to consider + this->PutRow(ev, r); + } + } + } + else // incremental write only table changes + { + morkList* list = &ioTable->mTable_ChangeList; + morkNext* next = list->GetListHead(); + while ( next && ev->Good() ) + { + this->PutTableChange(ev, (morkTableChange*) next); + next = next->GetNextLink(); + } + } + } + + if ( ev->Good() ) + this->EndTable(ev); + + ioTable->SetTableClean(ev); // note this also cleans change list + mWriter_TableRowScope = 0; + + ++mWriter_DoneCount; + return ev->Good(); +} + +mork_bool +morkWriter::PutTableDict(morkEnv* ev, morkTable* ioTable) +{ + morkRowSpace* space = ioTable->mTable_RowSpace; + mWriter_TableRowScope = space->SpaceScope(); + mWriter_TableForm = 0; // (f=iso-8859-1) + mWriter_TableAtomScope = 'v'; // (a=v) + mWriter_TableKind = ioTable->mTable_Kind; + + mWriter_RowForm = mWriter_TableForm; + mWriter_RowAtomScope = mWriter_TableAtomScope; + mWriter_RowScope = mWriter_TableRowScope; + + mWriter_DictForm = mWriter_TableForm; + mWriter_DictAtomScope = mWriter_TableAtomScope; + + // if ( ev->Good() ) + // this->StartDict(ev); // delay as long as possible + + if ( ev->Good() ) + { + morkRow* r = ioTable->mTable_MetaRow; + if ( r ) + { + if ( r->IsRow() ) + this->PutRowDict(ev, r); + else + r->NonRowTypeError(ev); + } + morkArray* array = &ioTable->mTable_RowArray; // vector of rows + mork_fill fill = array->mArray_Fill; // count of rows + morkRow** rows = (morkRow**) array->mArray_Slots; + if ( rows && fill ) + { + morkRow** end = rows + fill; + while ( rows < end && ev->Good() ) + { + r = *rows++; // next row to consider + if ( r && r->IsRow() ) + this->PutRowDict(ev, r); + else + r->NonRowTypeError(ev); + } + } + // we may have a change for a row which is no longer in the + // table, but contains a cell with something not in the dictionary. + // So, loop through the rows in the change log, writing out any + // dirty dictionary elements. + morkList* list = &ioTable->mTable_ChangeList; + morkNext* next = list->GetListHead(); + while ( next && ev->Good() ) + { + r = ((morkTableChange*) next)->mTableChange_Row; + if ( r && r->IsRow() ) + this->PutRowDict(ev, r); + next = next->GetNextLink(); + } + } + if ( ev->Good() ) + this->EndDict(ev); + + return ev->Good(); +} + +void +morkWriter::WriteTokenToTokenMetaCell(morkEnv* ev, + mork_token inCol, mork_token inValue) +{ + morkStream* stream = mWriter_Stream; + mork_bool isKindCol = ( morkStore_kKindColumn == inCol ); + mork_u1 valSep = (mork_u1) (( isKindCol )? '^' : '='); + + char buf[ 128 ]; // buffer for staging the two hex IDs + char* p = buf; + + mork_size bytesWritten; + if ( inCol < 0x80 ) + { + stream->Putc(ev, '('); + stream->Putc(ev, (char) inCol); + stream->Putc(ev, valSep); + } + else + { + *p++ = '('; // we always start with open paren + + *p++ = '^'; // indicates col is hex ID + mork_size colSize = ev->TokenAsHex(p, inCol); + p += colSize; + *p++ = (char) valSep; + stream->Write(ev->AsMdbEnv(), buf, colSize + 3, &bytesWritten); + + mWriter_LineSize += bytesWritten; + } + + if ( isKindCol ) + { + p = buf; + mork_size valSize = ev->TokenAsHex(p, inValue); + p += valSize; + *p++ = ':'; + *p++ = 'c'; + *p++ = ')'; + stream->Write(ev->AsMdbEnv(), buf, valSize + 3, &bytesWritten); + mWriter_LineSize += bytesWritten; + } + else + { + this->IndentAsNeeded(ev, morkWriter_kTableMetaCellValueDepth); + mdbYarn* yarn = &mWriter_ColYarn; + // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf; + mWriter_Store->TokenToString(ev, inValue, yarn); + this->WriteYarn(ev, yarn); + stream->Putc(ev, ')'); + ++mWriter_LineSize; + } + + // mork_fill fill = yarn->mYarn_Fill; + // yarnBuf[ fill ] = ')'; // append terminator + // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')' +} + +void +morkWriter::WriteStringToTokenDictCell(morkEnv* ev, + const char* inCol, mork_token inValue) + // Note inCol should begin with '(' and end with '=', with col in between. +{ + morkStream* stream = mWriter_Stream; + mWriter_LineSize += stream->PutString(ev, inCol); + + this->IndentAsNeeded(ev, morkWriter_kDictMetaCellValueDepth); + mdbYarn* yarn = &mWriter_ColYarn; + // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf; + mWriter_Store->TokenToString(ev, inValue, yarn); + this->WriteYarn(ev, yarn); + stream->Putc(ev, ')'); + ++mWriter_LineSize; + + // mork_fill fill = yarn->mYarn_Fill; + // yarnBuf[ fill ] = ')'; // append terminator + // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')' +} + +void +morkWriter::ChangeDictAtomScope(morkEnv* ev, mork_scope inScope) +{ + if ( inScope != mWriter_DictAtomScope ) + { + ev->NewWarning("unexpected atom scope change"); + + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[ 128 ]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '<'; // we always start with open paren + *p++ = '('; // we always start with open paren + *p++ = (char) morkStore_kAtomScopeColumn; + + mork_size scopeSize = 1; // default to one byte + if ( inScope >= 0x80 ) + { + *p++ = '^'; // indicates col is hex ID + scopeSize = ev->TokenAsHex(p, inScope); + p += scopeSize; + } + else + { + *p++ = '='; // indicates col is imm byte + *p++ = (char) (mork_u1) inScope; + } + + *p++ = ')'; + *p++ = '>'; + *p = 0; + + mork_size pending = scopeSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + mork_size bytesWritten; + + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_DictAtomScope = inScope; + } +} + +void +morkWriter::ChangeRowForm(morkEnv* ev, mork_cscode inNewForm) +{ + if ( inNewForm != mWriter_RowForm ) + { + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[ 128 ]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '['; // we always start with open bracket + *p++ = '('; // we always start with open paren + *p++ = (char) morkStore_kFormColumn; + + mork_size formSize = 1; // default to one byte + if (! morkCh_IsValue(inNewForm)) + { + *p++ = '^'; // indicates col is hex ID + formSize = ev->TokenAsHex(p, inNewForm); + p += formSize; + } + else + { + *p++ = '='; // indicates col is imm byte + *p++ = (char) (mork_u1) inNewForm; + } + + *p++ = ')'; + *p++ = ']'; + *p = 0; + + mork_size pending = formSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_RowForm = inNewForm; + } +} + +void +morkWriter::ChangeDictForm(morkEnv* ev, mork_cscode inNewForm) +{ + if ( inNewForm != mWriter_DictForm ) + { + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + char buf[ 128 ]; // buffer for staging the two hex IDs + char* p = buf; + *p++ = '<'; // we always start with open angle + *p++ = '('; // we always start with open paren + *p++ = (char) morkStore_kFormColumn; + + mork_size formSize = 1; // default to one byte + if (! morkCh_IsValue(inNewForm)) + { + *p++ = '^'; // indicates col is hex ID + formSize = ev->TokenAsHex(p, inNewForm); + p += formSize; + } + else + { + *p++ = '='; // indicates col is imm byte + *p++ = (char) (mork_u1) inNewForm; + } + + *p++ = ')'; + *p++ = '>'; + *p = 0; + + mork_size pending = formSize + 6; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth); + + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mWriter_DictForm = inNewForm; + } +} + +void +morkWriter::StartDict(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_DidStartDict ) + { + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } + mWriter_DidStartDict = morkBool_kTrue; + mWriter_DidEndDict = morkBool_kFalse; + + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + + if ( mWriter_TableRowScope ) // blank line before table's dict? + stream->PutLineBreak(ev); + + if ( mWriter_DictForm || mWriter_DictAtomScope != 'v' ) + { + stream->Putc(ev, '<'); + stream->Putc(ev, ' '); + stream->Putc(ev, '<'); + mWriter_LineSize = 3; + if ( mWriter_DictForm ) + this->WriteStringToTokenDictCell(ev, "(f=", mWriter_DictForm); + if ( mWriter_DictAtomScope != 'v' ) + this->WriteStringToTokenDictCell(ev, "(a=", mWriter_DictAtomScope); + + stream->Putc(ev, '>'); + ++mWriter_LineSize; + + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth); + } + else + { + stream->Putc(ev, '<'); + // stream->Putc(ev, ' '); + ++mWriter_LineSize; + } +} + +void +morkWriter::EndDict(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + if ( mWriter_DidStartDict ) + { + stream->Putc(ev, '>'); // end dict + ++mWriter_LineSize; + } + mWriter_DidStartDict = morkBool_kFalse; + mWriter_DidEndDict = morkBool_kTrue; +} + +void +morkWriter::StartTable(morkEnv* ev, morkTable* ioTable) +{ + mdbOid toid; // to receive table oid + ioTable->GetTableOid(ev, &toid); + + if ( ev->Good() ) + { + morkStream* stream = mWriter_Stream; + if ( mWriter_LineSize ) + stream->PutLineBreak(ev); + mWriter_LineSize = 0; + // stream->PutLineBreak(ev); + + char buf[ 64 + 16 ]; // buffer for staging hex + char* p = buf; + *p++ = '{'; // punct 1 + mork_size punctSize = (mWriter_BeVerbose) ? 10 : 3; // counting "{ {/*r=*/ " + + if ( ioTable->IsTableRewrite() && mWriter_Incremental ) + { + *p++ = '-'; + ++punctSize; // counting '-' // punct ++ + ++mWriter_LineSize; + } + mork_size oidSize = ev->OidAsHex(p, toid); + p += oidSize; + *p++ = ' '; // punct 2 + *p++ = '{'; // punct 3 + if (mWriter_BeVerbose) + { + + *p++ = '/'; // punct=4 + *p++ = '*'; // punct=5 + *p++ = 'r'; // punct=6 + *p++ = '='; // punct=7 + + mork_token tableUses = (mork_token) ioTable->mTable_GcUses; + mork_size usesSize = ev->TokenAsHex(p, tableUses); + punctSize += usesSize; + p += usesSize; + + *p++ = '*'; // punct=8 + *p++ = '/'; // punct=9 + *p++ = ' '; // punct=10 + } + mork_size bytesWritten; + + stream->Write(ev->AsMdbEnv(), buf, oidSize + punctSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + + mork_kind tk = mWriter_TableKind; + if ( tk ) + { + this->IndentAsNeeded(ev, morkWriter_kTableMetaCellDepth); + this->WriteTokenToTokenMetaCell(ev, morkStore_kKindColumn, tk); + } + + stream->Putc(ev, '('); // start 's' col cell + stream->Putc(ev, 's'); // column + stream->Putc(ev, '='); // column + mWriter_LineSize += 3; + + int prio = (int) ioTable->mTable_Priority; + if ( prio > 9 ) // need to force down to max decimal digit? + prio = 9; + prio += '0'; // add base digit zero + stream->Putc(ev, prio); // priority: (s=0 + ++mWriter_LineSize; + + if ( ioTable->IsTableUnique() ) + { + stream->Putc(ev, 'u'); // (s=0u + ++mWriter_LineSize; + } + if ( ioTable->IsTableVerbose() ) + { + stream->Putc(ev, 'v'); // (s=0uv + ++mWriter_LineSize; + } + + // stream->Putc(ev, ':'); // (s=0uv: + // stream->Putc(ev, 'c'); // (s=0uv:c + stream->Putc(ev, ')'); // end 's' col cell (s=0uv:c) + mWriter_LineSize += 1; // maybe 3 if we add ':' and 'c' + + morkRow* r = ioTable->mTable_MetaRow; + if ( r ) + { + if ( r->IsRow() ) + { + mWriter_SuppressDirtyRowNewline = morkBool_kTrue; + this->PutRow(ev, r); + } + else + r->NonRowTypeError(ev); + } + + stream->Putc(ev, '}'); // end meta + ++mWriter_LineSize; + + if ( mWriter_LineSize < mWriter_MaxIndent ) + { + stream->Putc(ev, ' '); // nice white space + ++mWriter_LineSize; + } + } +} + +void +morkWriter::EndTable(morkEnv* ev) +{ + morkStream* stream = mWriter_Stream; + stream->Putc(ev, '}'); // end table + ++mWriter_LineSize; + + mWriter_TableAtomScope = 'v'; // (a=v) +} + +mork_bool +morkWriter::PutRowDict(morkEnv* ev, morkRow* ioRow) +{ + mWriter_RowForm = mWriter_TableForm; + + morkCell* cells = ioRow->mRow_Cells; + if ( cells ) + { + morkStream* stream = mWriter_Stream; + mdbYarn yarn; // to ref content inside atom + char buf[ 64 ]; // buffer for staging the dict alias hex ID + char* idBuf = buf + 1; // where the id always starts + buf[ 0 ] = '('; // we always start with open paren + + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end && ev->Good() ) + { + morkAtom* atom = cells->GetAtom(); + if ( atom && atom->IsAtomDirty() ) + { + if ( atom->IsBook() ) // is it possible to write atom ID? + { + if ( !this->DidStartDict() ) + { + this->StartDict(ev); + if ( ev->Bad() ) + break; + } + atom->SetAtomClean(); // neutralize change + + this->IndentAsNeeded(ev, morkWriter_kDictAliasDepth); + morkBookAtom* ba = (morkBookAtom*) atom; + mork_size size = ev->TokenAsHex(idBuf, ba->mBookAtom_Id); + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, size+1, &bytesWritten); // '(' + mWriter_LineSize += bytesWritten; + + if ( morkAtom::AliasYarn(atom, &yarn) ) + { + mork_scope atomScope = atom->GetBookAtomSpaceScope(ev); + if ( atomScope && atomScope != mWriter_DictAtomScope ) + this->ChangeDictAtomScope(ev, atomScope); + + if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm ) + this->ChangeDictForm(ev, yarn.mYarn_Form); + + mork_size pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop + 1; + this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth); + + stream->Putc(ev, '='); // start value + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + + stream->Putc(ev, ')'); // end value + ++mWriter_LineSize; + } + else + atom->BadAtomKindError(ev); + + ++mWriter_DoneCount; + } + } + } + } + return ev->Good(); +} + +mork_bool +morkWriter::IsYarnAllValue(const mdbYarn* inYarn) +{ + mork_fill fill = inYarn->mYarn_Fill; + const mork_u1* buf = (const mork_u1*) inYarn->mYarn_Buf; + const mork_u1* end = buf + fill; + --buf; // prepare for preincrement + while ( ++buf < end ) + { + mork_ch c = *buf; + if ( !morkCh_IsValue(c) ) + return morkBool_kFalse; + } + return morkBool_kTrue; +} + +mork_bool +morkWriter::PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal) +{ + morkStream* stream = mWriter_Stream; + morkStore* store = mWriter_Store; + + mdbYarn* colYarn = &mWriter_ColYarn; + + morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0; + + mork_column col = ioCell->GetColumn(); + store->TokenToString(ev, col, colYarn); + + mdbYarn yarn; // to ref content inside atom + morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil + + if ( yarn.mYarn_Form != mWriter_RowForm ) + this->ChangeRowForm(ev, yarn.mYarn_Form); + + mork_size pending = yarn.mYarn_Fill + colYarn->mYarn_Fill + + morkWriter_kYarnEscapeSlop + 3; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + + stream->Putc(ev, '('); // start cell + ++mWriter_LineSize; + + this->WriteYarn(ev, colYarn); // column + + pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellValueDepth); + stream->Putc(ev, '='); + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); // value + + stream->Putc(ev, ')'); // end cell + ++mWriter_LineSize; + + return ev->Good(); +} + +mork_bool +morkWriter::PutVerboseRowCells(morkEnv* ev, morkRow* ioRow) +{ + morkCell* cells = ioRow->mRow_Cells; + if ( cells ) + { + + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end && ev->Good() ) + { + // note we prefer to avoid writing cells here with no value: + if ( cells->GetAtom() ) // does cell have any value? + this->PutVerboseCell(ev, cells, /*inWithVal*/ morkBool_kTrue); + } + } + return ev->Good(); +} + + +mork_bool +morkWriter::PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal) +{ + morkStream* stream = mWriter_Stream; + char buf[ 128 ]; // buffer for staging hex ids + char* idBuf = buf + 2; // where the id always starts + buf[ 0 ] = '('; // we always start with open paren + buf[ 1 ] = '^'; // column is always a hex ID + + mork_size colSize = 0; // the size of col hex ID + mork_size bytesWritten; + + morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0; + + mork_column col = ioCell->GetColumn(); + char* p = idBuf; + colSize = ev->TokenAsHex(p, col); + p += colSize; + + mdbYarn yarn; // to ref content inside atom + morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil + + if ( yarn.mYarn_Form != mWriter_RowForm ) + this->ChangeRowForm(ev, yarn.mYarn_Form); + + if ( atom && atom->IsBook() ) // is it possible to write atom ID? + { + this->IndentAsNeeded(ev, morkWriter_kRowCellDepth); + *p++ = '^'; + morkBookAtom* ba = (morkBookAtom*) atom; + + mork_size valSize = ev->TokenAsHex(p, ba->mBookAtom_Id); + mork_fill yarnFill = yarn.mYarn_Fill; + mork_bool putImmYarn = ( yarnFill <= valSize ); + if ( putImmYarn ) + putImmYarn = this->IsYarnAllValue(&yarn); + + if ( putImmYarn ) // value no bigger than id? + { + p[ -1 ] = '='; // go back and clobber '^' with '=' instead + if ( yarnFill ) + { + MORK_MEMCPY(p, yarn.mYarn_Buf, yarnFill); + p += yarnFill; + } + *p++ = ')'; + mork_size distance = (mork_size) (p - buf); + stream->Write(ev->AsMdbEnv(), buf, distance, &bytesWritten); + mWriter_LineSize += bytesWritten; + } + else + { + p += valSize; + *p = ')'; + stream->Write(ev->AsMdbEnv(), buf, colSize + valSize + 4, &bytesWritten); + mWriter_LineSize += bytesWritten; + } + + if ( atom->IsAtomDirty() ) + { + atom->SetAtomClean(); + ++mWriter_DoneCount; + } + } + else // must write an anonymous atom + { + mork_size pending = yarn.mYarn_Fill + colSize + + morkWriter_kYarnEscapeSlop + 2; + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + + mork_size bytesWritten; + stream->Write(ev->AsMdbEnv(), buf, colSize + 2, &bytesWritten); + mWriter_LineSize += bytesWritten; + + pending -= ( colSize + 2 ); + this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth); + stream->Putc(ev, '='); + ++mWriter_LineSize; + + this->WriteYarn(ev, &yarn); + stream->Putc(ev, ')'); // end cell + ++mWriter_LineSize; + } + return ev->Good(); +} + +mork_bool +morkWriter::PutRowCells(morkEnv* ev, morkRow* ioRow) +{ + morkCell* cells = ioRow->mRow_Cells; + if ( cells ) + { + morkCell* end = cells + ioRow->mRow_Length; + --cells; // prepare for preincrement: + while ( ++cells < end && ev->Good() ) + { + // note we prefer to avoid writing cells here with no value: + if ( cells->GetAtom() ) // does cell have any value? + this->PutCell(ev, cells, /*inWithVal*/ morkBool_kTrue); + } + } + return ev->Good(); +} + +mork_bool +morkWriter::PutRow(morkEnv* ev, morkRow* ioRow) +{ + if ( ioRow && ioRow->IsRow() ) + { + mWriter_RowForm = mWriter_TableForm; + + mork_size bytesWritten; + morkStream* stream = mWriter_Stream; + char buf[ 128 + 16 ]; // buffer for staging hex + char* p = buf; + mdbOid* roid = &ioRow->mRow_Oid; + mork_size ridSize = 0; + + mork_scope tableScope = mWriter_TableRowScope; + + if ( ioRow->IsRowDirty() ) + { + if ( mWriter_SuppressDirtyRowNewline || !mWriter_LineSize ) + mWriter_SuppressDirtyRowNewline = morkBool_kFalse; + else + { + if ( tableScope ) // in a table? + mWriter_LineSize = stream->PutIndent(ev, morkWriter_kRowDepth); + else + mWriter_LineSize = stream->PutIndent(ev, 0); // no indent + } + +// mork_rid rid = roid->mOid_Id; + *p++ = '['; // start row punct=1 + mork_size punctSize = (mWriter_BeVerbose) ? 9 : 1; // counting "[ /*r=*/ " + + mork_bool rowRewrite = ioRow->IsRowRewrite(); + + if ( rowRewrite && mWriter_Incremental ) + { + *p++ = '-'; + ++punctSize; // counting '-' + ++mWriter_LineSize; + } + + if ( tableScope && roid->mOid_Scope == tableScope ) + ridSize = ev->TokenAsHex(p, roid->mOid_Id); + else + ridSize = ev->OidAsHex(p, *roid); + + p += ridSize; + + if (mWriter_BeVerbose) + { + *p++ = ' '; // punct=2 + *p++ = '/'; // punct=3 + *p++ = '*'; // punct=4 + *p++ = 'r'; // punct=5 + *p++ = '='; // punct=6 + + mork_size usesSize = ev->TokenAsHex(p, (mork_token) ioRow->mRow_GcUses); + punctSize += usesSize; + p += usesSize; + + *p++ = '*'; // punct=7 + *p++ = '/'; // punct=8 + *p++ = ' '; // punct=9 + } + stream->Write(ev->AsMdbEnv(), buf, ridSize + punctSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + + // special case situation where row puts exactly one column: + if ( !rowRewrite && mWriter_Incremental && ioRow->HasRowDelta() ) + { + mork_column col = ioRow->GetDeltaColumn(); + morkCell dummy(col, morkChange_kNil, (morkAtom*) 0); + morkCell* cell = 0; + + mork_bool withVal = ( ioRow->GetDeltaChange() != morkChange_kCut ); + + if ( withVal ) + { + mork_pos cellPos = 0; // dummy pos + cell = ioRow->GetCell(ev, col, &cellPos); + } + if ( !cell ) + cell = &dummy; + + if ( mWriter_BeVerbose ) + this->PutVerboseCell(ev, cell, withVal); + else + this->PutCell(ev, cell, withVal); + } + else // put entire row? + { + if ( mWriter_BeVerbose ) + this->PutVerboseRowCells(ev, ioRow); // write all, verbosely + else + this->PutRowCells(ev, ioRow); // write all, hex notation + } + + stream->Putc(ev, ']'); // end row + ++mWriter_LineSize; + } + else + { + this->IndentAsNeeded(ev, morkWriter_kRowDepth); + + if ( tableScope && roid->mOid_Scope == tableScope ) + ridSize = ev->TokenAsHex(p, roid->mOid_Id); + else + ridSize = ev->OidAsHex(p, *roid); + + stream->Write(ev->AsMdbEnv(), buf, ridSize, &bytesWritten); + mWriter_LineSize += bytesWritten; + stream->Putc(ev, ' '); + ++mWriter_LineSize; + } + + ++mWriter_DoneCount; + + ioRow->SetRowClean(); // try to do this at the very last + } + else + ioRow->NonRowTypeWarning(ev); + + return ev->Good(); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + diff --git a/db/mork/src/morkWriter.h b/db/mork/src/morkWriter.h new file mode 100644 index 0000000000..dea0ced836 --- /dev/null +++ b/db/mork/src/morkWriter.h @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKWRITER_ +#define _MORKWRITER_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKMAP_ +#include "morkMap.h" +#endif + +#ifndef _MORKROWMAP_ +#include "morkRowMap.h" +#endif + +#ifndef _MORKTABLE_ +#include "morkTable.h" +#endif + +#ifndef _MORKATOMMAP_ +#include "morkAtomMap.h" +#endif + +#ifndef _MORKATOMSPACE_ +#include "morkAtomSpace.h" +#endif + +#ifndef _MORKROWSPACE_ +#include "morkRowSpace.h" +#endif + +#ifndef _MORKSTREAM_ +#include "morkStream.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +#define morkWriter_kStreamBufSize /*i*/ (16 * 1024) /* buffer size for stream */ + +#define morkDerived_kWriter /*i*/ 0x5772 /* ascii 'Wr' */ + +#define morkWriter_kPhaseNothingDone 0 /* nothing has yet been done */ +#define morkWriter_kPhaseDirtyAllDone 1 /* DirtyAll() is done */ +#define morkWriter_kPhasePutHeaderDone 2 /* PutHeader() is done */ + +#define morkWriter_kPhaseRenumberAllDone 3 /* RenumberAll() is done */ + +#define morkWriter_kPhaseStoreAtomSpaces 4 /*mWriter_StoreAtomSpacesIter*/ +#define morkWriter_kPhaseAtomSpaceAtomAids 5 /*mWriter_AtomSpaceAtomAidsIter*/ + +#define morkWriter_kPhaseStoreRowSpacesTables 6 /*mWriter_StoreRowSpacesIter*/ +#define morkWriter_kPhaseRowSpaceTables 7 /*mWriter_RowSpaceTablesIter*/ +#define morkWriter_kPhaseTableRowArray 8 /*mWriter_TableRowArrayPos */ + +#define morkWriter_kPhaseStoreRowSpacesRows 9 /*mWriter_StoreRowSpacesIter*/ +#define morkWriter_kPhaseRowSpaceRows 10 /*mWriter_RowSpaceRowsIter*/ + +#define morkWriter_kPhaseContentDone 11 /* all content written */ +#define morkWriter_kPhaseWritingDone 12 /* everthing has been done */ + +#define morkWriter_kCountNumberOfPhases 13 /* part of mWrite_TotalCount */ + +#define morkWriter_kMaxColumnNameSize 128 /* longest writable col name */ + +#define morkWriter_kMaxIndent 66 /* default value for mWriter_MaxIndent */ +#define morkWriter_kMaxLine 78 /* default value for mWriter_MaxLine */ + +#define morkWriter_kYarnEscapeSlop 4 /* guess average yarn escape overhead */ + +#define morkWriter_kTableMetaCellDepth 4 /* */ +#define morkWriter_kTableMetaCellValueDepth 6 /* */ + +#define morkWriter_kDictMetaCellDepth 4 /* */ +#define morkWriter_kDictMetaCellValueDepth 6 /* */ + +#define morkWriter_kDictAliasDepth 2 /* */ +#define morkWriter_kDictAliasValueDepth 4 /* */ + +#define morkWriter_kRowDepth 2 /* */ +#define morkWriter_kRowCellDepth 4 /* */ +#define morkWriter_kRowCellValueDepth 6 /* */ + +#define morkWriter_kGroupBufSize 64 /* */ + +// v=1.1 retired on 23-Mar-99 (for metainfo one char column names) +// v=1.2 retired on 20-Apr-99 (for ":c" suffix on table kind hex refs) +// v=1.3 retired on 20-Apr-99 (for 1CE:m instead of ill-formed 1CE:^6D) +#define morkWriter_kFileHeader "// " + +class morkWriter : public morkNode { // row iterator + +// public: // slots inherited from morkObject (meant to inform only) + // nsIMdbHeap* mNode_Heap; + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + + morkStore* mWriter_Store; // weak ref to committing store + nsIMdbFile* mWriter_File; // strong ref to store's file + nsIMdbFile* mWriter_Bud; // strong ref to bud of mWriter_File + morkStream* mWriter_Stream; // strong ref to stream on bud file + nsIMdbHeap* mWriter_SlotHeap; // strong ref to slot heap + + // GroupIdentity should be based on mStore_CommitGroupIdentity: + mork_gid mWriter_CommitGroupIdentity; // transaction ID number + + // GroupBuf holds a hex version of mWriter_CommitGroupIdentity: + char mWriter_GroupBuf[ morkWriter_kGroupBufSize ]; + mork_fill mWriter_GroupBufFill; // actual bytes in GroupBuf + + mork_count mWriter_TotalCount; // count of all things to be written + mork_count mWriter_DoneCount; // count of things already written + + mork_size mWriter_LineSize; // length of current line being written + mork_size mWriter_MaxIndent; // line size forcing a line break + mork_size mWriter_MaxLine; // line size forcing a value continuation + + mork_cscode mWriter_TableForm; // current charset metainfo + mork_scope mWriter_TableAtomScope; // current atom scope + mork_scope mWriter_TableRowScope; // current row scope + mork_kind mWriter_TableKind; // current table kind + + mork_cscode mWriter_RowForm; // current charset metainfo + mork_scope mWriter_RowAtomScope; // current atom scope + mork_scope mWriter_RowScope; // current row scope + + mork_cscode mWriter_DictForm; // current charset metainfo + mork_scope mWriter_DictAtomScope; // current atom scope + + mork_bool mWriter_NeedDirtyAll; // need to call DirtyAll() + mork_bool mWriter_Incremental; // opposite of mWriter_NeedDirtyAll + mork_bool mWriter_DidStartDict; // true when a dict has been started + mork_bool mWriter_DidEndDict; // true when a dict has been ended + + mork_bool mWriter_SuppressDirtyRowNewline; // for table meta rows + mork_bool mWriter_DidStartGroup; // true when a group has been started + mork_bool mWriter_DidEndGroup; // true when a group has been ended + mork_u1 mWriter_Phase; // status of writing process + + mork_bool mWriter_BeVerbose; // driven by env and table verbose settings: + // mWriter_BeVerbose equals ( ev->mEnv_BeVerbose || table->IsTableVerbose() ) + + mork_u1 mWriter_Pad[ 3 ]; // for u4 alignment + + mork_pos mWriter_TableRowArrayPos; // index into mTable_RowArray + + char mWriter_SafeNameBuf[ (morkWriter_kMaxColumnNameSize * 2) + 4 ]; + // Note: extra four bytes in ColNameBuf means we can always append to yarn + + char mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize + 4 ]; + // Note: extra four bytes in ColNameBuf means we can always append to yarn + + mdbYarn mWriter_ColYarn; // a yarn to describe space in ColNameBuf: + // mYarn_Buf == mWriter_ColNameBuf, mYarn_Size == morkWriter_kMaxColumnNameSize + + mdbYarn mWriter_SafeYarn; // a yarn to describe space in ColNameBuf: + // mYarn_Buf == mWriter_SafeNameBuf, mYarn_Size == (kMaxColumnNameSize * 2) + + morkAtomSpaceMapIter mWriter_StoreAtomSpacesIter; // for mStore_AtomSpaces + morkAtomAidMapIter mWriter_AtomSpaceAtomAidsIter; // for AtomSpace_AtomAids + + morkRowSpaceMapIter mWriter_StoreRowSpacesIter; // for mStore_RowSpaces + morkTableMapIter mWriter_RowSpaceTablesIter; // for mRowSpace_Tables + +#ifdef MORK_ENABLE_PROBE_MAPS + morkRowProbeMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows +#else /*MORK_ENABLE_PROBE_MAPS*/ + morkRowMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows +#endif /*MORK_ENABLE_PROBE_MAPS*/ + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseWriter() + virtual ~morkWriter(); // assert that close executed earlier + +public: // morkWriter construction & destruction + morkWriter(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile, + nsIMdbHeap* ioSlotHeap); + void CloseWriter(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkWriter(const morkWriter& other); + morkWriter& operator=(const morkWriter& other); + +public: // dynamic type identification + mork_bool IsWriter() const + { return IsNode() && mNode_Derived == morkDerived_kWriter; } +// } ===== end morkNode methods ===== + +public: // typing & errors + static void NonWriterTypeError(morkEnv* ev); + static void NilWriterStoreError(morkEnv* ev); + static void NilWriterBudError(morkEnv* ev); + static void NilWriterStreamError(morkEnv* ev); + static void NilWriterFileError(morkEnv* ev); + static void UnsupportedPhaseError(morkEnv* ev); + +public: // utitlities + void ChangeRowForm(morkEnv* ev, mork_cscode inNewForm); + void ChangeDictForm(morkEnv* ev, mork_cscode inNewForm); + void ChangeDictAtomScope(morkEnv* ev, mork_scope inScope); + +public: // inlines + mork_bool DidStartDict() const { return mWriter_DidStartDict; } + mork_bool DidEndDict() const { return mWriter_DidEndDict; } + + void IndentAsNeeded(morkEnv* ev, mork_size inDepth) + { + if ( mWriter_LineSize > mWriter_MaxIndent ) + mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth); + } + + void IndentOverMaxLine(morkEnv* ev, + mork_size inPendingSize, mork_size inDepth) + { + if ( mWriter_LineSize + inPendingSize > mWriter_MaxLine ) + mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth); + } + +public: // delayed construction + + void MakeWriterStream(morkEnv* ev); // give writer a suitable stream + +public: // iterative/asynchronous writing + + mork_bool WriteMore(morkEnv* ev); // call until IsWritingDone() is true + + mork_bool IsWritingDone() const // don't call WriteMore() any longer? + { return mWriter_Phase == morkWriter_kPhaseWritingDone; } + +public: // marking all content dirty + mork_bool DirtyAll(morkEnv* ev); + // DirtyAll() visits every store sub-object and marks + // them dirty, including every table, row, cell, and atom. The return + // equals ev->Good(), to show whether any error happened. This method is + // intended for use in the beginning of a "compress commit" which writes + // all store content, whether dirty or not. We dirty everything first so + // that later iterations over content can mark things clean as they are + // written, and organize the process of serialization so that objects are + // written only at need (because of being dirty). Note the method can + // stop early when any error happens, since this will abort any commit. + +public: // group commit transactions + + mork_bool StartGroup(morkEnv* ev); + mork_bool CommitGroup(morkEnv* ev); + mork_bool AbortGroup(morkEnv* ev); + +public: // phase methods + mork_bool OnNothingDone(morkEnv* ev); + mork_bool OnDirtyAllDone(morkEnv* ev); + mork_bool OnPutHeaderDone(morkEnv* ev); + + mork_bool OnRenumberAllDone(morkEnv* ev); + + mork_bool OnStoreAtomSpaces(morkEnv* ev); + mork_bool OnAtomSpaceAtomAids(morkEnv* ev); + + mork_bool OnStoreRowSpacesTables(morkEnv* ev); + mork_bool OnRowSpaceTables(morkEnv* ev); + mork_bool OnTableRowArray(morkEnv* ev); + + mork_bool OnStoreRowSpacesRows(morkEnv* ev); + mork_bool OnRowSpaceRows(morkEnv* ev); + + mork_bool OnContentDone(morkEnv* ev); + mork_bool OnWritingDone(morkEnv* ev); + +public: // writing dict items first pass + mork_bool PutTableDict(morkEnv* ev, morkTable* ioTable); + mork_bool PutRowDict(morkEnv* ev, morkRow* ioRow); + +public: // writing node content second pass + mork_bool PutTable(morkEnv* ev, morkTable* ioTable); + mork_bool PutRow(morkEnv* ev, morkRow* ioRow); + mork_bool PutRowCells(morkEnv* ev, morkRow* ioRow); + mork_bool PutVerboseRowCells(morkEnv* ev, morkRow* ioRow); + + mork_bool PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal); + mork_bool PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal); + + mork_bool PutTableChange(morkEnv* ev, const morkTableChange* inChange); + +public: // other writer methods + + mork_bool IsYarnAllValue(const mdbYarn* inYarn); + + mork_size WriteYarn(morkEnv* ev, const mdbYarn* inYarn); + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire yarn's size, since only part goes on a last line). + + mork_size WriteAtom(morkEnv* ev, const morkAtom* inAtom); + // return number of atom bytes written on the current line (which + // implies that escaped line breaks will make the size value smaller + // than the entire atom's size, since only part goes on a last line). + + void WriteAllStoreTables(morkEnv* ev); + void WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace); + + void WriteTokenToTokenMetaCell(morkEnv* ev, mork_token inCol, + mork_token inValue); + void WriteStringToTokenDictCell(morkEnv* ev, const char* inCol, + mork_token inValue); + // Note inCol should begin with '(' and end with '=', with col in between. + + void StartDict(morkEnv* ev); + void EndDict(morkEnv* ev); + + void StartTable(morkEnv* ev, morkTable* ioTable); + void EndTable(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakWriter(morkWriter* me, + morkEnv* ev, morkWriter** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongWriter(morkWriter* me, + morkEnv* ev, morkWriter** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKTABLEROWCURSOR_ */ diff --git a/db/mork/src/morkYarn.cpp b/db/mork/src/morkYarn.cpp new file mode 100644 index 0000000000..ce52a741e8 --- /dev/null +++ b/db/mork/src/morkYarn.cpp @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#ifndef _MORKYARN_ +#include "morkYarn.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// ````` ````` ````` ````` ````` +// { ===== begin morkNode interface ===== + +/*public virtual*/ void +morkYarn::CloseMorkNode(morkEnv* ev) /*i*/ // CloseYarn() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseYarn(ev); + this->MarkShut(); + } +} + +/*public virtual*/ +morkYarn::~morkYarn() /*i*/ // assert CloseYarn() executed earlier +{ + MORK_ASSERT(mYarn_Body.mYarn_Buf==0); +} + +/*public non-poly*/ +morkYarn::morkYarn(morkEnv* ev, /*i*/ + const morkUsage& inUsage, nsIMdbHeap* ioHeap) + : morkNode(ev, inUsage, ioHeap) +{ + if ( ev->Good() ) + mNode_Derived = morkDerived_kYarn; +} + +/*public non-poly*/ void +morkYarn::CloseYarn(morkEnv* ev) /*i*/ // called by CloseMorkNode(); +{ + if ( this->IsNode() ) + this->MarkShut(); + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== +// ````` ````` ````` ````` ````` + +/*static*/ void +morkYarn::NonYarnTypeError(morkEnv* ev) +{ + ev->NewError("non morkYarn"); +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/morkYarn.h b/db/mork/src/morkYarn.h new file mode 100644 index 0000000000..a5b38bbf38 --- /dev/null +++ b/db/mork/src/morkYarn.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKYARN_ +#define _MORKYARN_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +#define morkDerived_kYarn /*i*/ 0x7952 /* ascii 'yR' */ + +/*| morkYarn: a reference counted nsIMdbYarn C struct. This is for use in those +**| few cases where single instances of reference counted buffers are needed +**| in Mork, and we expect few enough instances that overhead is not a factor +**| in decided whether to use such a thing. +|*/ +class morkYarn : public morkNode { // refcounted yarn + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + mdbYarn mYarn_Body; + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseYarn() only if open + virtual ~morkYarn(); // assert that CloseYarn() executed earlier + +public: // morkYarn construction & destruction + morkYarn(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap); + void CloseYarn(morkEnv* ev); // called by CloseMorkNode(); + +private: // copying is not allowed + morkYarn(const morkYarn& other); + morkYarn& operator=(const morkYarn& other); + +public: // dynamic type identification + mork_bool IsYarn() const + { return IsNode() && mNode_Derived == morkDerived_kYarn; } +// } ===== end morkNode methods ===== + +public: // typing + static void NonYarnTypeError(morkEnv* ev); + +public: // typesafe refcounting inlines calling inherited morkNode methods + static void SlotWeakYarn(morkYarn* me, + morkEnv* ev, morkYarn** ioSlot) + { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); } + + static void SlotStrongYarn(morkYarn* me, + morkEnv* ev, morkYarn** ioSlot) + { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); } +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKYARN_ */ diff --git a/db/mork/src/morkZone.cpp b/db/mork/src/morkZone.cpp new file mode 100644 index 0000000000..239e86bba0 --- /dev/null +++ b/db/mork/src/morkZone.cpp @@ -0,0 +1,527 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKZONE_ +#include "morkZone.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { ===== begin morkNode interface ===== +// public: // morkNode virtual methods +void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open +{ + if ( this->IsOpenNode() ) + { + this->MarkClosing(); + this->CloseZone(ev); + this->MarkShut(); + } +} + +morkZone::~morkZone() // assert that CloseZone() executed earlier +{ + MORK_ASSERT(this->IsShutNode()); +} + +// public: // morkMap construction & destruction +morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage, + nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap) +: morkNode(ev, inUsage, ioNodeHeap) +, mZone_Heap( 0 ) +, mZone_HeapVolume( 0 ) +, mZone_BlockVolume( 0 ) +, mZone_RunVolume( 0 ) +, mZone_ChipVolume( 0 ) + +, mZone_FreeOldRunVolume( 0 ) + +, mZone_HunkCount( 0 ) +, mZone_FreeOldRunCount( 0 ) + +, mZone_HunkList( 0 ) +, mZone_FreeOldRunList( 0 ) + +, mZone_At( 0 ) +, mZone_AtSize( 0 ) + + // morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ]; +{ + + morkRun** runs = mZone_FreeRuns; + morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot + --runs; // prepare for preincrement + while ( ++runs < end ) // another slot in array? + *runs = 0; // clear all the slots + + if ( ev->Good() ) + { + if ( ioZoneHeap ) + { + nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap); + if ( ev->Good() ) + mNode_Derived = morkDerived_kZone; + } + else + ev->NilPointerError(); + } +} + +void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode() +{ + if ( this->IsNode() ) + { + nsIMdbHeap* heap = mZone_Heap; + if ( heap ) + { + morkHunk* hunk = 0; + nsIMdbEnv* mev = ev->AsMdbEnv(); + + morkHunk* next = mZone_HunkList; + while ( ( hunk = next ) != 0 ) + { +#ifdef morkHunk_USE_TAG_SLOT + if ( !hunk->HunkGoodTag() ) + hunk->BadHunkTagWarning(ev); +#endif /* morkHunk_USE_TAG_SLOT */ + + next = hunk->HunkNext(); + heap->Free(mev, hunk); + } + } + nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mZone_Heap); + this->MarkShut(); + } + else + this->NonNodeError(ev); +} + +// } ===== end morkNode methods ===== + +/*static*/ void +morkZone::NonZoneTypeError(morkEnv* ev) +{ + ev->NewError("non morkZone"); +} + +/*static*/ void +morkZone::NilZoneHeapError(morkEnv* ev) +{ + ev->NewError("nil mZone_Heap"); +} + +/*static*/ void +morkHunk::BadHunkTagWarning(morkEnv* ev) +{ + ev->NewWarning("bad mHunk_Tag"); +} + +/*static*/ void +morkRun::BadRunTagError(morkEnv* ev) +{ + ev->NewError("bad mRun_Tag"); +} + +/*static*/ void +morkRun::RunSizeAlignError(morkEnv* ev) +{ + ev->NewError("bad RunSize() alignment"); +} + +// { ===== begin morkZone methods ===== + + +mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize) +{ + mZone_At = 0; // remove any ref to current hunk + mZone_AtSize = 0; // zero available bytes in current hunk + + mork_size runSize = 0; // actual size of a particular run + + // try to find a run in old run list with at least inNeededSize bytes: + morkRun* run = mZone_FreeOldRunList; // cursor in list scan + morkRun* prev = 0; // the node before run in the list scan + + while ( run ) // another run in list to check? + { + morkOldRun* oldRun = (morkOldRun*) run; + mork_size oldSize = oldRun->OldSize(); + if ( oldSize >= inNeededSize ) // found one big enough? + { + runSize = oldSize; + break; // end while loop early + } + prev = run; // remember last position in singly linked list + run = run->RunNext(); // advance cursor to next node in list + } + if ( runSize && run ) // found a usable old run? + { + morkRun* next = run->RunNext(); + if ( prev ) // another node in free list precedes run? + prev->RunSetNext(next); // unlink run + else + mZone_FreeOldRunList = next; // unlink run from head of list + + morkOldRun *oldRun = (morkOldRun *) run; + oldRun->OldSetSize(runSize); + mZone_At = (mork_u1*) run; + mZone_AtSize = runSize; + +#ifdef morkZone_CONFIG_DEBUG +#ifdef morkZone_CONFIG_ALIGN_8 + mork_ip lowThree = ((mork_ip) mZone_At) & 7; + if ( lowThree ) // not 8 byte aligned? +#else /*morkZone_CONFIG_ALIGN_8*/ + mork_ip lowTwo = ((mork_ip) mZone_At) & 3; + if ( lowTwo ) // not 4 byte aligned? +#endif /*morkZone_CONFIG_ALIGN_8*/ + ev->NewWarning("mZone_At not aligned"); +#endif /*morkZone_CONFIG_DEBUG*/ + } + else // need to allocate a brand new run + { + inNeededSize += 7; // allow for possible alignment padding + mork_size newSize = ( inNeededSize > morkZone_kNewHunkSize )? + inNeededSize : morkZone_kNewHunkSize; + + morkHunk* hunk = this->zone_new_hunk(ev, newSize); + if ( hunk ) + { + morkRun* hunkRun = hunk->HunkRun(); + mork_u1* at = (mork_u1*) hunkRun->RunAsBlock(); + mork_ip lowBits = ((mork_ip) at) & 7; + if ( lowBits ) // not 8 byte aligned? + { + mork_ip skip = (8 - lowBits); // skip the complement to align + at += skip; + newSize -= skip; + } + mZone_At = at; + mZone_AtSize = newSize; + } + } + + return mZone_AtSize; +} + +morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc +{ + mdb_size hunkSize = inSize + sizeof(morkHunk); + void* outBlock = 0; // we are going straight to the heap: + mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock); + if ( outBlock ) + { +#ifdef morkZone_CONFIG_VOL_STATS + mZone_HeapVolume += hunkSize; // track all heap allocations +#endif /* morkZone_CONFIG_VOL_STATS */ + + morkHunk* hunk = (morkHunk*) outBlock; +#ifdef morkHunk_USE_TAG_SLOT + hunk->HunkInitTag(); +#endif /* morkHunk_USE_TAG_SLOT */ + + hunk->HunkSetNext(mZone_HunkList); + mZone_HunkList = hunk; + ++mZone_HunkCount; + + morkRun* run = hunk->HunkRun(); + run->RunSetSize(inSize); +#ifdef morkRun_USE_TAG_SLOT + run->RunInitTag(); +#endif /* morkRun_USE_TAG_SLOT */ + + return hunk; + } + if ( ev->Good() ) // got this far without any error reported yet? + ev->OutOfMemoryError(); + return (morkHunk*) 0; +} + +void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_VOL_STATS + mZone_BlockVolume += inSize; // sum sizes of both chips and runs +#endif /* morkZone_CONFIG_VOL_STATS */ + + mork_u1* at = mZone_At; + mork_size atSize = mZone_AtSize; // available bytes in current hunk + if ( atSize >= inSize ) // current hunk can satisfy request? + { + mZone_At = at + inSize; + mZone_AtSize = atSize - inSize; + return at; + } + else if ( atSize > morkZone_kMaxHunkWaste ) // over max waste allowed? + { + morkHunk* hunk = this->zone_new_hunk(ev, inSize); + if ( hunk ) + return hunk->HunkRun(); + + return (void*) 0; // show allocation has failed + } + else // get ourselves a new hunk for suballocation: + { + atSize = this->zone_grow_at(ev, inSize); // get a new hunk + } + + if ( atSize >= inSize ) // current hunk can satisfy request? + { + at = mZone_At; + mZone_At = at + inSize; + mZone_AtSize = atSize - inSize; + return at; + } + + if ( ev->Good() ) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*) 0; // show allocation has failed +} + +void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_ARENA + +#ifdef morkZone_CONFIG_DEBUG + if ( !this->IsZone() ) + this->NonZoneTypeError(ev); + else if ( !mZone_Heap ) + this->NilZoneHeapError(ev); +#endif /*morkZone_CONFIG_DEBUG*/ + +#ifdef morkZone_CONFIG_ALIGN_8 + inSize += 7; + inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes +#else /*morkZone_CONFIG_ALIGN_8*/ + inSize += 3; + inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes +#endif /*morkZone_CONFIG_ALIGN_8*/ + +#ifdef morkZone_CONFIG_VOL_STATS + mZone_ChipVolume += inSize; // sum sizes of chips only +#endif /* morkZone_CONFIG_VOL_STATS */ + + return this->zone_new_chip(ev, inSize); + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ + +} + +// public: // ...but runs do indeed know how big they are +void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc +{ +#ifdef morkZone_CONFIG_ARENA + +#ifdef morkZone_CONFIG_DEBUG + if ( !this->IsZone() ) + this->NonZoneTypeError(ev); + else if ( !mZone_Heap ) + this->NilZoneHeapError(ev); +#endif /*morkZone_CONFIG_DEBUG*/ + + inSize += morkZone_kRoundAdd; + inSize &= morkZone_kRoundMask; + if ( inSize <= morkZone_kMaxCachedRun ) + { + morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits); + morkRun* hit = *bucket; + if ( hit ) // cache hit? + { + *bucket = hit->RunNext(); + hit->RunSetSize(inSize); + return hit->RunAsBlock(); + } + } + mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead +#ifdef morkZone_CONFIG_VOL_STATS + mZone_RunVolume += blockSize; // sum sizes of runs only +#endif /* morkZone_CONFIG_VOL_STATS */ + morkRun* run = (morkRun*) this->zone_new_chip(ev, blockSize); + if ( run ) + { + run->RunSetSize(inSize); +#ifdef morkRun_USE_TAG_SLOT + run->RunInitTag(); +#endif /* morkRun_USE_TAG_SLOT */ + return run->RunAsBlock(); + } + + if ( ev->Good() ) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*) 0; // indicate failed allocation + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ +} + +void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free +{ +#ifdef morkZone_CONFIG_ARENA + + morkRun* run = morkRun::BlockAsRun(ioRunBlock); + mdb_size runSize = run->RunSize(); +#ifdef morkZone_CONFIG_VOL_STATS + mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs +#endif /* morkZone_CONFIG_VOL_STATS */ + +#ifdef morkZone_CONFIG_DEBUG + if ( !this->IsZone() ) + this->NonZoneTypeError(ev); + else if ( !mZone_Heap ) + this->NilZoneHeapError(ev); + else if ( !ioRunBlock ) + ev->NilPointerError(); + else if ( runSize & morkZone_kRoundAdd ) + run->RunSizeAlignError(ev); +#ifdef morkRun_USE_TAG_SLOT + else if ( !run->RunGoodTag() ) + run->BadRunTagError(ev); +#endif /* morkRun_USE_TAG_SLOT */ +#endif /*morkZone_CONFIG_DEBUG*/ + + if ( runSize <= morkZone_kMaxCachedRun ) // goes into free run list? + { + morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits); + run->RunSetNext(*bucket); // push onto free run list + *bucket = run; + } + else // free old run list + { + run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list + mZone_FreeOldRunList = run; + ++mZone_FreeOldRunCount; +#ifdef morkZone_CONFIG_VOL_STATS + mZone_FreeOldRunVolume += runSize; +#endif /* morkZone_CONFIG_VOL_STATS */ + + morkOldRun* oldRun = (morkOldRun*) run; // to access extra size slot + oldRun->OldSetSize(runSize); // so we know how big this is later + } + +#else /*morkZone_CONFIG_ARENA*/ + mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock); +#endif /*morkZone_CONFIG_ARENA*/ +} + +void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize) +{ +#ifdef morkZone_CONFIG_ARENA + + morkRun* run = morkRun::BlockAsRun(ioRunBlock); + mdb_size runSize = run->RunSize(); + +#ifdef morkZone_CONFIG_DEBUG + if ( !this->IsZone() ) + this->NonZoneTypeError(ev); + else if ( !mZone_Heap ) + this->NilZoneHeapError(ev); +#endif /*morkZone_CONFIG_DEBUG*/ + +#ifdef morkZone_CONFIG_ALIGN_8 + inSize += 7; + inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes +#else /*morkZone_CONFIG_ALIGN_8*/ + inSize += 3; + inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes +#endif /*morkZone_CONFIG_ALIGN_8*/ + + if ( inSize > runSize ) + { + void* newBuf = this->ZoneNewRun(ev, inSize); + if ( newBuf ) + { + MORK_MEMCPY(newBuf, ioRunBlock, runSize); + this->ZoneZapRun(ev, ioRunBlock); + + return newBuf; + } + } + else + return ioRunBlock; // old size is big enough + + if ( ev->Good() ) // got this far without any error reported yet? + ev->OutOfMemoryError(); + + return (void*) 0; // indicate failed allocation + +#else /*morkZone_CONFIG_ARENA*/ + void* outBlock = 0; + mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock); + return outBlock; +#endif /*morkZone_CONFIG_ARENA*/ +} + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +// { ===== begin nsIMdbHeap methods ===== +/*virtual*/ nsresult +morkZone::Alloc(nsIMdbEnv* mev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) // memory block of inSize bytes, or nil +{ + nsresult outErr = NS_OK; + void* block = 0; + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + block = this->ZoneNewRun(ev, inSize); + outErr = ev->AsErr(); + } + else + outErr = morkEnv_kOutOfMemoryError; + + if ( outBlock ) + *outBlock = block; + + return outErr; +} + +/*virtual*/ nsresult +morkZone::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc() + void* inBlock) +{ + nsresult outErr = NS_OK; + if ( inBlock ) + { + morkEnv* ev = morkEnv::FromMdbEnv(mev); + if ( ev ) + { + this->ZoneZapRun(ev, inBlock); + outErr = ev->AsErr(); + } + else + // XXX 1 is not a valid nsresult + outErr = static_cast(1); + } + + return outErr; +} + +// } ===== end nsIMdbHeap methods ===== + diff --git a/db/mork/src/morkZone.h b/db/mork/src/morkZone.h new file mode 100644 index 0000000000..9e5ffc54c4 --- /dev/null +++ b/db/mork/src/morkZone.h @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MORKZONE_ +#define _MORKZONE_ 1 + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _MORKNODE_ +#include "morkNode.h" +#endif + +#ifndef _MORKDEQUE_ +#include "morkDeque.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| CONFIG_DEBUG: do paranoid debug checks if defined. +|*/ +#ifdef MORK_DEBUG +#define morkZone_CONFIG_DEBUG 1 /* debug paranoid if defined */ +#endif /*MORK_DEBUG*/ + +/*| CONFIG_STATS: keep volume and usage statistics. +|*/ +#define morkZone_CONFIG_VOL_STATS 1 /* count space used by zone instance */ + +/*| CONFIG_ARENA: if this is defined, then the morkZone class will alloc big +**| blocks from the zone's heap, and suballocate from these. If undefined, +**| then morkZone will just pass all calls through to the zone's heap. +|*/ +#ifdef MORK_ENABLE_ZONE_ARENAS +#define morkZone_CONFIG_ARENA 1 /* be arena, if defined; otherwise no-op */ +#endif /*MORK_ENABLE_ZONE_ARENAS*/ + +/*| CONFIG_ALIGN_8: if this is defined, then the morkZone class will give +**| blocks 8 byte alignment instead of only 4 byte alignment. +|*/ +#ifdef MORK_CONFIG_ALIGN_8 +#define morkZone_CONFIG_ALIGN_8 1 /* ifdef: align to 8 bytes, otherwise 4 */ +#endif /*MORK_CONFIG_ALIGN_8*/ + +/*| CONFIG_PTR_SIZE_4: if this is defined, then the morkZone class will +**| assume sizeof(void*) == 4, so a tag slot for padding is needed. +|*/ +#ifdef MORK_CONFIG_PTR_SIZE_4 +#define morkZone_CONFIG_PTR_SIZE_4 1 /* ifdef: sizeof(void*) == 4 */ +#endif /*MORK_CONFIG_PTR_SIZE_4*/ + +/*| morkZone_USE_TAG_SLOT: if this is defined, then define slot mRun_Tag +**| in order to achieve eight byte alignment after the mRun_Next slot. +|*/ +#if defined(morkZone_CONFIG_ALIGN_8) && defined(morkZone_CONFIG_PTR_SIZE_4) +#define morkRun_USE_TAG_SLOT 1 /* need mRun_Tag slot inside morkRun */ +#define morkHunk_USE_TAG_SLOT 1 /* need mHunk_Tag slot inside morkHunk */ +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkRun_kTag ((mork_u4) 0x6D52754E ) /* ascii 'mRuN' */ + +/*| morkRun: structure used by morkZone for sized blocks +|*/ +class morkRun { + +protected: // member variable slots +#ifdef morkRun_USE_TAG_SLOT + mork_u4 mRun_Tag; // force 8 byte alignment after mRun_Next +#endif /* morkRun_USE_TAG_SLOT */ + + morkRun* mRun_Next; + +public: // pointer interpretation of mRun_Next (when inside a list): + morkRun* RunNext() const { return mRun_Next; } + void RunSetNext(morkRun* ioNext) { mRun_Next = ioNext; } + +public: // size interpretation of mRun_Next (when not inside a list): + mork_size RunSize() const { return (mork_size) ((mork_ip) mRun_Next); } + void RunSetSize(mork_size inSize) + { mRun_Next = (morkRun*) ((mork_ip) inSize); } + +public: // maintenance and testing of optional tag magic signature slot: +#ifdef morkRun_USE_TAG_SLOT + void RunInitTag() { mRun_Tag = morkRun_kTag; } + mork_bool RunGoodTag() { return ( mRun_Tag == morkRun_kTag ); } +#endif /* morkRun_USE_TAG_SLOT */ + +public: // conversion back and forth to inline block following run instance: + void* RunAsBlock() { return (((mork_u1*) this) + sizeof(morkRun)); } + + static morkRun* BlockAsRun(void* ioBlock) + { return (morkRun*) (((mork_u1*) ioBlock) - sizeof(morkRun)); } + +public: // typing & errors + static void BadRunTagError(morkEnv* ev); + static void RunSizeAlignError(morkEnv* ev); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +/*| morkOldRun: more space to record size when run is put into old free list +|*/ +class morkOldRun : public morkRun { + +protected: // need another size field when mRun_Next is used for linkage: + mdb_size mOldRun_Size; + +public: // size getter/setter + mork_size OldSize() const { return mOldRun_Size; } + void OldSetSize(mork_size inSize) { mOldRun_Size = inSize; } + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define morkHunk_kTag ((mork_u4) 0x68556E4B ) /* ascii 'hUnK' */ + +/*| morkHunk: structure used by morkZone for heap allocations. +|*/ +class morkHunk { + +protected: // member variable slots + +#ifdef morkHunk_USE_TAG_SLOT + mork_u4 mHunk_Tag; // force 8 byte alignment after mHunk_Next +#endif /* morkHunk_USE_TAG_SLOT */ + + morkHunk* mHunk_Next; + + morkRun mHunk_Run; + +public: // setters + void HunkSetNext(morkHunk* ioNext) { mHunk_Next = ioNext; } + +public: // getters + morkHunk* HunkNext() const { return mHunk_Next; } + + morkRun* HunkRun() { return &mHunk_Run; } + +public: // maintenance and testing of optional tag magic signature slot: +#ifdef morkHunk_USE_TAG_SLOT + void HunkInitTag() { mHunk_Tag = morkHunk_kTag; } + mork_bool HunkGoodTag() { return ( mHunk_Tag == morkHunk_kTag ); } +#endif /* morkHunk_USE_TAG_SLOT */ + +public: // typing & errors + static void BadHunkTagWarning(morkEnv* ev); + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +/*| kNewHunkSize: the default size for a hunk, assuming we must allocate +**| a new one whenever the free hunk list does not already have. Note this +**| number should not be changed without also considering suitable changes +**| in the related kMaxHunkWaste and kMinHunkSize constants. +|*/ +#define morkZone_kNewHunkSize ((mork_size) (64 * 1024)) /* 64K per hunk */ + +/*| kMaxFreeVolume: some number of bytes of free space in the free hunk list +**| over which we no longer want to add more free hunks to the list, for fear +**| of accumulating too much unused, fragmented free space. This should be a +**| small multiple of kNewHunkSize, say about two to four times as great, to +**| allow for no more free hunk space than fits in a handful of new hunks. +**| This strategy will let us usefuly accumulate "some" free space in the +**| free hunk list, but without accumulating "too much" free space that way. +|*/ +#define morkZone_kMaxFreeVolume (morkZone_kNewHunkSize * 3) + +/*| kMaxHunkWaste: if a current request is larger than this, and we cannot +**| satisfy the request with the current hunk, then we just allocate the +**| block from the heap without changing the current hunk. Basically this +**| number represents the largest amount of memory we are willing to waste, +**| since a block request barely less than this can cause the current hunk +**| to be retired (with any unused space wasted) as well get a new hunk. +|*/ +#define morkZone_kMaxHunkWaste ((mork_size) 4096) /* 1/16 kNewHunkSize */ + +/*| kRound*: the algorithm for rounding up allocation sizes for caching +**| in free lists works like the following. We add kRoundAdd to any size +**| requested, and then bitwise AND with kRoundMask, and this will give us +**| the smallest multiple of kRoundSize that is at least as large as the +**| requested size. Then if we rightshift this number by kRoundBits, we +**| will have the index into the mZone_FreeRuns array which will hold any +**| cache runs of that size. So 4 bits of shift gives us a granularity +**| of 16 bytes, so that free lists will hold successive runs that are +**| 16 bytes greater than the next smaller run size. If we have 256 free +**| lists of nonzero sized runs altogether, then the largest run that can +**| be cached is 4096, or 4K (since 4096 == 16 * 256). A larger run that +**| gets freed will go in to the free hunk list (or back to the heap). +|*/ +#define morkZone_kRoundBits 4 /* bits to round-up size for free lists */ +#define morkZone_kRoundSize (1 << morkZone_kRoundBits) +#define morkZone_kRoundAdd ((1 << morkZone_kRoundBits) - 1) +#define morkZone_kRoundMask (~ ((mork_ip) morkZone_kRoundAdd)) + +#define morkZone_kBuckets 256 /* number of distinct free lists */ + +/*| kMaxCachedRun: the largest run that will be stored inside a free +**| list of old zapped runs. A run larger than this cannot be put in +**| a free list, and must be allocated from the heap at need, and put +**| into the free hunk list when discarded. +|*/ +#define morkZone_kMaxCachedRun (morkZone_kBuckets * morkZone_kRoundSize) + +#define morkDerived_kZone /*i*/ 0x5A6E /* ascii 'Zn' */ + +/*| morkZone: a pooled memory allocator like an NSPR arena. The term 'zone' +**| is roughly synonymous with 'heap'. I avoid calling this class a "heap" +**| to avoid any confusion with nsIMdbHeap, and I avoid calling this class +**| an arean to avoid confusion with NSPR usage. +|*/ +class morkZone : public morkNode, public nsIMdbHeap { + +// public: // slots inherited from morkNode (meant to inform only) + // nsIMdbHeap* mNode_Heap; + + // mork_base mNode_Base; // must equal morkBase_kNode + // mork_derived mNode_Derived; // depends on specific node subclass + + // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead + // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone + // mork_able mNode_Mutable; // can this node be modified? + // mork_load mNode_Load; // is this node clean or dirty? + + // mork_uses mNode_Uses; // refcount for strong refs + // mork_refs mNode_Refs; // refcount for strong refs + weak refs + +public: // state is public because the entire Mork system is private + + nsIMdbHeap* mZone_Heap; // strong ref to heap allocating all space + + mork_size mZone_HeapVolume; // total bytes allocated from heap + mork_size mZone_BlockVolume; // total bytes in all zone blocks + mork_size mZone_RunVolume; // total bytes in all zone runs + mork_size mZone_ChipVolume; // total bytes in all zone chips + + mork_size mZone_FreeOldRunVolume; // total bytes in all used hunks + + mork_count mZone_HunkCount; // total number of used hunks + mork_count mZone_FreeOldRunCount; // total free old runs + + morkHunk* mZone_HunkList; // linked list of all used hunks + morkRun* mZone_FreeOldRunList; // linked list of free old runs + + // note mZone_At is a byte pointer for single byte address arithmetic: + mork_u1* mZone_At; // current position in most recent hunk + mork_size mZone_AtSize; // number of bytes remaining in this hunk + + // kBuckets+1 so indexes zero through kBuckets are all okay to use: + + morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ]; + // Each piece of memory stored in list mZone_FreeRuns[ i ] has an + // allocation size equal to sizeof(morkRun) + (i * kRoundSize), so + // that callers can be given a piece of memory with (i * kRoundSize) + // bytes of writeable space while reserving the first sizeof(morkRun) + // bytes to keep track of size information for later re-use. Note + // that mZone_FreeRuns[ 0 ] is unused because no run will be zero + // bytes in size (and morkZone plans to complain about zero sizes). + +protected: // zone utilities + + mork_size zone_grow_at(morkEnv* ev, mork_size inNeededSize); + + void* zone_new_chip(morkEnv* ev, mdb_size inSize); // alloc + morkHunk* zone_new_hunk(morkEnv* ev, mdb_size inRunSize); // alloc + +// { ===== begin nsIMdbHeap methods ===== +public: + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) override; // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock) override; + + virtual size_t GetUsedSize() override { return mZone_Heap->GetUsedSize(); } +// } ===== end nsIMdbHeap methods ===== + +// { ===== begin morkNode interface ===== +public: // morkNode virtual methods + virtual void CloseMorkNode(morkEnv* ev) override; // CloseZone() only if open + virtual ~morkZone(); // assert that CloseMap() executed earlier + +public: // morkMap construction & destruction + morkZone(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap, + nsIMdbHeap* ioZoneHeap); + + void CloseZone(morkEnv* ev); // called by CloseMorkNode() + +public: // dynamic type identification + mork_bool IsZone() const + { return IsNode() && mNode_Derived == morkDerived_kZone; } +// } ===== end morkNode methods ===== + +// { ===== begin morkZone methods ===== +public: // chips do not know how big they are... + void* ZoneNewChip(morkEnv* ev, mdb_size inSize); // alloc + +public: // ...but runs do indeed know how big they are + void* ZoneNewRun(morkEnv* ev, mdb_size inSize); // alloc + void ZoneZapRun(morkEnv* ev, void* ioRunBody); // free + void* ZoneGrowRun(morkEnv* ev, void* ioRunBody, mdb_size inSize); // realloc + +// } ===== end morkZone methods ===== + +public: // typing & errors + static void NonZoneTypeError(morkEnv* ev); + static void NilZoneHeapError(morkEnv* ev); + static void BadZoneTagError(morkEnv* ev); +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _MORKZONE_ */ diff --git a/db/mork/src/moz.build b/db/mork/src/moz.build new file mode 100644 index 0000000000..0fb9645d93 --- /dev/null +++ b/db/mork/src/moz.build @@ -0,0 +1,55 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'morkArray.cpp', + 'morkAtom.cpp', + 'morkAtomMap.cpp', + 'morkAtomSpace.cpp', + 'morkBead.cpp', + 'morkBlob.cpp', + 'morkBuilder.cpp', + 'morkCell.cpp', + 'morkCellObject.cpp', + 'morkCh.cpp', + 'morkConfig.cpp', + 'morkCursor.cpp', + 'morkDeque.cpp', + 'morkEnv.cpp', + 'morkFactory.cpp', + 'morkFile.cpp', + 'morkHandle.cpp', + 'morkIntMap.cpp', + 'morkMap.cpp', + 'morkNode.cpp', + 'morkNodeMap.cpp', + 'morkObject.cpp', + 'morkParser.cpp', + 'morkPool.cpp', + 'morkPortTableCursor.cpp', + 'morkProbeMap.cpp', + 'morkRow.cpp', + 'morkRowCellCursor.cpp', + 'morkRowMap.cpp', + 'morkRowObject.cpp', + 'morkRowSpace.cpp', + 'morkSink.cpp', + 'morkSpace.cpp', + 'morkStore.cpp', + 'morkStream.cpp', + 'morkTable.cpp', + 'morkTableRowCursor.cpp', + 'morkThumb.cpp', + 'morkWriter.cpp', + 'morkYarn.cpp', + 'morkZone.cpp', + 'orkinHeap.cpp', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + SOURCES += ['morkSearchRowCursor.cpp'] + +FINAL_LIBRARY = 'mork' + diff --git a/db/mork/src/orkinHeap.cpp b/db/mork/src/orkinHeap.cpp new file mode 100644 index 0000000000..4a309a154f --- /dev/null +++ b/db/mork/src/orkinHeap.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +#ifndef _ORKINHEAP_ +#include "orkinHeap.h" +#endif + +#ifndef _MORKENV_ +#include "morkEnv.h" +#endif + +#include "nsIMemoryReporter.h" + +#include + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + + +orkinHeap::orkinHeap() // does nothing + : mUsedSize(0) +{ +} + +/*virtual*/ +orkinHeap::~orkinHeap() // does nothing +{ +} + +MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MorkSizeOfOnAlloc) +MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MorkSizeOfOnFree) + +// { ===== begin nsIMdbHeap methods ===== +/*virtual*/ nsresult +orkinHeap::Alloc(nsIMdbEnv* mev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock) // memory block of inSize bytes, or nil +{ + + MORK_USED_1(mev); + nsresult outErr = NS_OK; + void* block = malloc(inSize); + if ( !block ) + outErr = morkEnv_kOutOfMemoryError; + else + mUsedSize += MorkSizeOfOnAlloc(block); + + MORK_ASSERT(outBlock); + if ( outBlock ) + *outBlock = block; + return outErr; +} + +/*virtual*/ nsresult +orkinHeap::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc() + void* inBlock) +{ + MORK_USED_1(mev); + MORK_ASSERT(inBlock); + if ( inBlock ) + { + mUsedSize -= MorkSizeOfOnFree(inBlock); + free(inBlock); + } + return NS_OK; +} + +size_t +orkinHeap::GetUsedSize() +{ + return mUsedSize; +} +// } ===== end nsIMdbHeap methods ===== + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/orkinHeap.h b/db/mork/src/orkinHeap.h new file mode 100644 index 0000000000..a3c14d2954 --- /dev/null +++ b/db/mork/src/orkinHeap.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _ORKINHEAP_ +#define _ORKINHEAP_ 1 + +#ifndef _MDB_ +#include "mdb.h" +#endif + +#ifndef _MORK_ +#include "mork.h" +#endif + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#define orkinHeap_kTag 0x68456150 /* ascii 'hEaP' */ + +/*| orkinHeap: +|*/ +class orkinHeap : public nsIMdbHeap { // +protected: + size_t mUsedSize; + +public: + orkinHeap(); // does nothing + virtual ~orkinHeap(); // does nothing + +private: // copying is not allowed + orkinHeap(const orkinHeap& other); + orkinHeap& operator=(const orkinHeap& other); + +public: + +// { ===== begin nsIMdbHeap methods ===== + NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory + mdb_size inSize, // requested size of new memory block + void** outBlock); // memory block of inSize bytes, or nil + + NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() + void* inBlock); + + virtual size_t GetUsedSize(); +// } ===== end nsIMdbHeap methods ===== + +}; + +//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 + +#endif /* _ORKINHEAP_ */ diff --git a/db/moz.build b/db/moz.build new file mode 100644 index 0000000000..2f360fad64 --- /dev/null +++ b/db/moz.build @@ -0,0 +1,11 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if not CONFIG['NSS_DISABLE_DBM'] and CONFIG['MOZ_MORK']: + DIRS += [ + 'mork/public', + 'mork/src', + 'mork/build', + ] diff --git a/ldap/c-sdk/include/disptmpl.h b/ldap/c-sdk/include/disptmpl.h new file mode 100644 index 0000000000..b287e3f953 --- /dev/null +++ b/ldap/c-sdk/include/disptmpl.h @@ -0,0 +1,379 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1993, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + * + * disptmpl.h: display template library defines + */ + +#ifndef _DISPTMPL_H +#define _DISPTMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* calling conventions used by library */ +#ifndef LDAP_CALL +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_C __cdecl +#ifndef _WIN32 +#define __stdcall _far _pascal +#define LDAP_CALLBACK _loadds +#else +#define LDAP_CALLBACK +#endif /* _WIN32 */ +#define LDAP_PASCAL __stdcall +#define LDAP_CALL LDAP_PASCAL +#else /* _WINDOWS */ +#define LDAP_C +#define LDAP_CALLBACK +#define LDAP_PASCAL +#define LDAP_CALL +#endif /* _WINDOWS */ +#endif /* LDAP_CALL */ + +#define LDAP_TEMPLATE_VERSION 1 + +/* + * general types of items (confined to most significant byte) + */ +#define LDAP_SYN_TYPE_TEXT 0x01000000L +#define LDAP_SYN_TYPE_IMAGE 0x02000000L +#define LDAP_SYN_TYPE_BOOLEAN 0x04000000L +#define LDAP_SYN_TYPE_BUTTON 0x08000000L +#define LDAP_SYN_TYPE_ACTION 0x10000000L + + +/* + * syntax options (confined to second most significant byte) + */ +#define LDAP_SYN_OPT_DEFER 0x00010000L + + +/* + * display template item syntax ids (defined by common agreement) + * these are the valid values for the ti_syntaxid of the tmplitem + * struct (defined below). A general type is encoded in the + * most-significant 8 bits, and some options are encoded in the next + * 8 bits. The lower 16 bits are reserved for the distinct types. + */ +#define LDAP_SYN_CASEIGNORESTR ( 1 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_MULTILINESTR ( 2 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_DN ( 3 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_BOOLEAN ( 4 | LDAP_SYN_TYPE_BOOLEAN ) +#define LDAP_SYN_JPEGIMAGE ( 5 | LDAP_SYN_TYPE_IMAGE ) +#define LDAP_SYN_JPEGBUTTON ( 6 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER ) +#define LDAP_SYN_FAXIMAGE ( 7 | LDAP_SYN_TYPE_IMAGE ) +#define LDAP_SYN_FAXBUTTON ( 8 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER ) +#define LDAP_SYN_AUDIOBUTTON ( 9 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER ) +#define LDAP_SYN_TIME ( 10 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_DATE ( 11 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_LABELEDURL ( 12 | LDAP_SYN_TYPE_TEXT ) +#define LDAP_SYN_SEARCHACTION ( 13 | LDAP_SYN_TYPE_ACTION ) +#define LDAP_SYN_LINKACTION ( 14 | LDAP_SYN_TYPE_ACTION ) +#define LDAP_SYN_ADDDNACTION ( 15 | LDAP_SYN_TYPE_ACTION ) +#define LDAP_SYN_VERIFYDNACTION ( 16 | LDAP_SYN_TYPE_ACTION ) +#define LDAP_SYN_RFC822ADDR ( 17 | LDAP_SYN_TYPE_TEXT ) + + +/* + * handy macros + */ +#define LDAP_GET_SYN_TYPE( syid ) ((syid) & 0xFF000000UL ) +#define LDAP_GET_SYN_OPTIONS( syid ) ((syid) & 0x00FF0000UL ) + + +/* + * display options for output routines (used by entry2text and friends) + */ +/* + * use calculated label width (based on length of longest label in + * template) instead of contant width + */ +#define LDAP_DISP_OPT_AUTOLABELWIDTH 0x00000001L +#define LDAP_DISP_OPT_HTMLBODYONLY 0x00000002L + +/* + * perform search actions (applies to ldap_entry2text_search only) + */ +#define LDAP_DISP_OPT_DOSEARCHACTIONS 0x00000002L + +/* + * include additional info. relevant to "non leaf" entries only + * used by ldap_entry2html and ldap_entry2html_search to include "Browse" + * and "Move Up" HREFs + */ +#define LDAP_DISP_OPT_NONLEAF 0x00000004L + + +/* + * display template item options (may not apply to all types) + * if this bit is set in ti_options, it applies. + */ +#define LDAP_DITEM_OPT_READONLY 0x00000001L +#define LDAP_DITEM_OPT_SORTVALUES 0x00000002L +#define LDAP_DITEM_OPT_SINGLEVALUED 0x00000004L +#define LDAP_DITEM_OPT_HIDEIFEMPTY 0x00000008L +#define LDAP_DITEM_OPT_VALUEREQUIRED 0x00000010L +#define LDAP_DITEM_OPT_HIDEIFFALSE 0x00000020L /* booleans only */ + + + +/* + * display template item structure + */ +struct ldap_tmplitem { + unsigned long ti_syntaxid; + unsigned long ti_options; + char *ti_attrname; + char *ti_label; + char **ti_args; + struct ldap_tmplitem *ti_next_in_row; + struct ldap_tmplitem *ti_next_in_col; + void *ti_appdata; +}; + + +#define NULLTMPLITEM ((struct ldap_tmplitem *)0) + +#define LDAP_SET_TMPLITEM_APPDATA( ti, datap ) \ + (ti)->ti_appdata = (void *)(datap) + +#define LDAP_GET_TMPLITEM_APPDATA( ti, type ) \ + (type)((ti)->ti_appdata) + +#define LDAP_IS_TMPLITEM_OPTION_SET( ti, option ) \ + (((ti)->ti_options & option ) != 0 ) + + +/* + * object class array structure + */ +struct ldap_oclist { + char **oc_objclasses; + struct ldap_oclist *oc_next; +}; + +#define NULLOCLIST ((struct ldap_oclist *)0) + + +/* + * add defaults list + */ +struct ldap_adddeflist { + int ad_source; +#define LDAP_ADSRC_CONSTANTVALUE 1 +#define LDAP_ADSRC_ADDERSDN 2 + char *ad_attrname; + char *ad_value; + struct ldap_adddeflist *ad_next; +}; + +#define NULLADLIST ((struct ldap_adddeflist *)0) + + +/* + * display template global options + * if this bit is set in dt_options, it applies. + */ +/* + * users should be allowed to try to add objects of these entries + */ +#define LDAP_DTMPL_OPT_ADDABLE 0x00000001L + +/* + * users should be allowed to do "modify RDN" operation of these entries + */ +#define LDAP_DTMPL_OPT_ALLOWMODRDN 0x00000002L + +/* + * this template is an alternate view, not a primary view + */ +#define LDAP_DTMPL_OPT_ALTVIEW 0x00000004L + + +/* + * display template structure + */ +struct ldap_disptmpl { + char *dt_name; + char *dt_pluralname; + char *dt_iconname; + unsigned long dt_options; + char *dt_authattrname; + char *dt_defrdnattrname; + char *dt_defaddlocation; + struct ldap_oclist *dt_oclist; + struct ldap_adddeflist *dt_adddeflist; + struct ldap_tmplitem *dt_items; + void *dt_appdata; + struct ldap_disptmpl *dt_next; +}; + +#define NULLDISPTMPL ((struct ldap_disptmpl *)0) + +#define LDAP_SET_DISPTMPL_APPDATA( dt, datap ) \ + (dt)->dt_appdata = (void *)(datap) + +#define LDAP_GET_DISPTMPL_APPDATA( dt, type ) \ + (type)((dt)->dt_appdata) + +#define LDAP_IS_DISPTMPL_OPTION_SET( dt, option ) \ + (((dt)->dt_options & option ) != 0 ) + +#define LDAP_TMPL_ERR_VERSION 1 +#define LDAP_TMPL_ERR_MEM 2 +#define LDAP_TMPL_ERR_SYNTAX 3 +#define LDAP_TMPL_ERR_FILE 4 + +/* + * buffer size needed for entry2text and vals2text + */ +#define LDAP_DTMPL_BUFSIZ 8192 + +typedef int (*writeptype)( void *writeparm, char *p, int len ); + +LDAP_API(int) +LDAP_CALL +ldap_init_templates( char *file, struct ldap_disptmpl **tmpllistp ); + +LDAP_API(int) +LDAP_CALL +ldap_init_templates_buf( char *buf, long buflen, + struct ldap_disptmpl **tmpllistp ); + +LDAP_API(void) +LDAP_CALL +ldap_free_templates( struct ldap_disptmpl *tmpllist ); + +LDAP_API(struct ldap_disptmpl *) +LDAP_CALL +ldap_first_disptmpl( struct ldap_disptmpl *tmpllist ); + +LDAP_API(struct ldap_disptmpl *) +LDAP_CALL +ldap_next_disptmpl( struct ldap_disptmpl *tmpllist, + struct ldap_disptmpl *tmpl ); + +LDAP_API(struct ldap_disptmpl *) +LDAP_CALL +ldap_name2template( char *name, struct ldap_disptmpl *tmpllist ); + +LDAP_API(struct ldap_disptmpl *) +LDAP_CALL +ldap_oc2template( char **oclist, struct ldap_disptmpl *tmpllist ); + +LDAP_API(char **) +LDAP_CALL +ldap_tmplattrs( struct ldap_disptmpl *tmpl, char **includeattrs, int exclude, + unsigned long syntaxmask ); + +LDAP_API(struct ldap_tmplitem *) +LDAP_CALL +ldap_first_tmplrow( struct ldap_disptmpl *tmpl ); + +LDAP_API(struct ldap_tmplitem *) +LDAP_CALL +ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row ); + +LDAP_API(struct ldap_tmplitem *) +LDAP_CALL +ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row ); + +LDAP_API(struct ldap_tmplitem *) +LDAP_CALL +ldap_next_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row, + struct ldap_tmplitem *col ); + +LDAP_API(int) +LDAP_CALL +ldap_entry2text( LDAP *ld, char *buf, LDAPMessage *entry, + struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals, + writeptype writeproc, void *writeparm, char *eol, int rdncount, + unsigned long opts ); + +LDAP_API(int) +LDAP_CALL +ldap_vals2text( LDAP *ld, char *buf, char **vals, char *label, int labelwidth, + unsigned long syntaxid, writeptype writeproc, void *writeparm, + char *eol, int rdncount ); + +LDAP_API(int) +LDAP_CALL +ldap_entry2text_search( LDAP *ld, char *dn, char *base, LDAPMessage *entry, + struct ldap_disptmpl *tmpllist, char **defattrs, char ***defvals, + writeptype writeproc, void *writeparm, char *eol, int rdncount, + unsigned long opts ); + +LDAP_API(int) +LDAP_CALL +ldap_entry2html( LDAP *ld, char *buf, LDAPMessage *entry, + struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals, + writeptype writeproc, void *writeparm, char *eol, int rdncount, + unsigned long opts, char *urlprefix, char *base ); + +LDAP_API(int) +LDAP_CALL +ldap_vals2html( LDAP *ld, char *buf, char **vals, char *label, int labelwidth, + unsigned long syntaxid, writeptype writeproc, void *writeparm, + char *eol, int rdncount, char *urlprefix ); + +LDAP_API(int) +LDAP_CALL +ldap_entry2html_search( LDAP *ld, char *dn, char *base, LDAPMessage *entry, + struct ldap_disptmpl *tmpllist, char **defattrs, char ***defvals, + writeptype writeproc, void *writeparm, char *eol, int rdncount, + unsigned long opts, char *urlprefix ); + +LDAP_API(char *) +LDAP_CALL +ldap_tmplerr2string( int err ); + +#ifdef __cplusplus +} +#endif +#endif /* _DISPTMPL_H */ diff --git a/ldap/c-sdk/include/iutil.h b/ldap/c-sdk/include/iutil.h new file mode 100644 index 0000000000..a87a800d2d --- /dev/null +++ b/ldap/c-sdk/include/iutil.h @@ -0,0 +1,72 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Interface for libiutil the innosoft migration library + * + */ + +#ifndef _IUTIL_H +#define _IUTIL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* from iutil-lock.c */ + +#ifdef _WINDOWS +#define LDAP_MUTEX_T HANDLE + +extern char *ldap_strdup(); +extern unsigned char *ldap_utf8_nextchar(); +extern char **ldap_explode_ava(); +extern int ldap_utf8_toupper(); + +int pthread_mutex_init( LDAP_MUTEX_T *mp, void *attr); +static void * pthread_mutex_alloc( void ); +int pthread_mutex_destroy( LDAP_MUTEX_T *mp ); +static void pthread_mutex_free( void *mutexp ); +int pthread_mutex_lock( LDAP_MUTEX_T *mp ); +int pthread_mutex_unlock( LDAP_MUTEX_T *mp ); + +#endif /* _WINDOWS */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _IUTIL_H */ diff --git a/ldap/c-sdk/include/lber.h b/ldap/c-sdk/include/lber.h new file mode 100644 index 0000000000..4ad1daafd4 --- /dev/null +++ b/ldap/c-sdk/include/lber.h @@ -0,0 +1,339 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* lber.h - header file for ber_* functions */ +#ifndef _LBER_H +#define _LBER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* to pick up size_t typedef */ + +/* + * Note that LBER_ERROR and LBER_DEFAULT are values that can never appear + * as valid BER tags, and so it is safe to use them to report errors. In + * fact, any tag for which the following is true is invalid: + * (( tag & 0x00000080 ) != 0 ) && (( tag & 0xFFFFFF00 ) != 0 ) + */ +#define LBER_ERROR ((ber_tag_t) -1) /* 0xffffffffU */ +#define LBER_DEFAULT ((ber_tag_t) -1) /* 0xffffffffU */ +#define LBER_END_OF_SEQORSET ((ber_tag_t) -2) /* 0xfffffffeU */ +#define LBER_OVERFLOW ((ber_tag_t) -3) /* 0xfffffffdU */ + +/* BER classes and mask */ +#define LBER_CLASS_UNIVERSAL 0x00 +#define LBER_CLASS_APPLICATION 0x40 +#define LBER_CLASS_CONTEXT 0x80 +#define LBER_CLASS_PRIVATE 0xc0 +#define LBER_CLASS_MASK 0xc0 + +/* BER encoding type and mask */ +#define LBER_PRIMITIVE 0x00 +#define LBER_CONSTRUCTED 0x20 +#define LBER_ENCODING_MASK 0x20 + +#define LBER_BIG_TAG_MASK 0x1f +#define LBER_MORE_TAG_MASK 0x80 + +/* general BER types we know about */ +#define LBER_BOOLEAN 0x01 +#define LBER_INTEGER 0x02 +#define LBER_BITSTRING 0x03 +#define LBER_OCTETSTRING 0x04 +#define LBER_NULL 0x05 +#define LBER_ENUMERATED 0x0a +#define LBER_SEQUENCE 0x30 +#define LBER_SET 0x31 + +/* BerElement set/get options */ +#define LBER_OPT_REMAINING_BYTES 0x01 +#define LBER_OPT_TOTAL_BYTES 0x02 +#define LBER_OPT_USE_DER 0x04 +#define LBER_OPT_TRANSLATE_STRINGS 0x08 +#define LBER_OPT_BYTES_TO_WRITE 0x10 +#define LBER_OPT_MEMALLOC_FN_PTRS 0x20 +#define LBER_OPT_DEBUG_LEVEL 0x40 +#define LBER_OPT_BUFSIZE 0x80 + +/* + * LBER_USE_DER is defined for compatibility with the C LDAP API RFC. + * In our implementation, we recognize it (instead of the numerically + * identical LBER_OPT_REMAINING_BYTES) in calls to ber_alloc_t() and + * ber_init_w_nullchar() only. Callers of ber_set_option() or + * ber_get_option() must use LBER_OPT_USE_DER instead. Sorry! + */ +#define LBER_USE_DER 0x01 + +/* Sockbuf set/get options */ +#define LBER_SOCKBUF_OPT_TO_FILE 0x001 +#define LBER_SOCKBUF_OPT_TO_FILE_ONLY 0x002 +#define LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE 0x004 +#define LBER_SOCKBUF_OPT_NO_READ_AHEAD 0x008 +#define LBER_SOCKBUF_OPT_DESC 0x010 +#define LBER_SOCKBUF_OPT_COPYDESC 0x020 +#define LBER_SOCKBUF_OPT_READ_FN 0x040 +#define LBER_SOCKBUF_OPT_WRITE_FN 0x080 +#define LBER_SOCKBUF_OPT_EXT_IO_FNS 0x100 +#define LBER_SOCKBUF_OPT_VALID_TAG 0x200 +#define LBER_SOCKBUF_OPT_SOCK_ARG 0x400 + +#define LBER_OPT_ON ((void *) 1) +#define LBER_OPT_OFF ((void *) 0) + +typedef unsigned int ber_len_t; /* for BER len */ +typedef unsigned int ber_tag_t; /* for BER tags */ +typedef int ber_int_t; /* for BER ints, enums, and Booleans */ +typedef unsigned int ber_uint_t; /* unsigned equivalent of ber_int_t */ +typedef int ber_slen_t; /* signed equivalent of ber_len_t */ + +typedef struct berval { + ber_len_t bv_len; + char *bv_val; +} BerValue; + +typedef struct berelement BerElement; +typedef struct sockbuf Sockbuf; +typedef int (*BERTranslateProc)( char **bufp, ber_uint_t *buflenp, + int free_input ); +#ifndef macintosh +#if defined( _WINDOWS ) || defined( _WIN32) || defined( _CONSOLE ) +#include /* for SOCKET */ +typedef SOCKET LBER_SOCKET; +#else +typedef long LBER_SOCKET; +#endif /* _WINDOWS */ +#else /* macintosh */ +typedef void *LBER_SOCKET; +#endif /* macintosh */ + +/* calling conventions used by library */ +#ifndef LDAP_CALL +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_C __cdecl +#ifndef _WIN32 +#define __stdcall _far _pascal +#define LDAP_CALLBACK _loadds +#else +#define LDAP_CALLBACK +#endif /* _WIN32 */ +#define LDAP_PASCAL __stdcall +#define LDAP_CALL LDAP_PASCAL +#else /* _WINDOWS */ +#define LDAP_C +#define LDAP_CALLBACK +#define LDAP_PASCAL +#define LDAP_CALL +#endif /* _WINDOWS */ +#endif /* LDAP_CALL */ + +/* + * function prototypes for lber library + */ + +#ifndef LDAP_API +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_API(rt) rt +#else /* _WINDOWS */ +#define LDAP_API(rt) rt +#endif /* _WINDOWS */ +#endif /* LDAP_API */ + +struct lextiof_socket_private; /* Defined by the extended I/O */ + /* callback functions */ +struct lextiof_session_private; /* Defined by the extended I/O */ + /* callback functions */ + +/* This is modeled after the PRIOVec that is passed to the NSPR + writev function! The void* is a char* in that struct */ +typedef struct ldap_x_iovec { + char *ldapiov_base; + int ldapiov_len; +} ldap_x_iovec; + +/* + * libldap read and write I/O function callbacks. The rest of the I/O callback + * types are defined in ldap.h + */ +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_READ_CALLBACK)( LBER_SOCKET s, + void *buf, int bufsize ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_WRITE_CALLBACK)( LBER_SOCKET s, + const void *buf, int len ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_READ_CALLBACK)( int s, + void *buf, int bufsize, struct lextiof_socket_private *socketarg ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_WRITE_CALLBACK)( int s, + const void *buf, int len, struct lextiof_socket_private *socketarg ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_WRITEV_CALLBACK)(int s, + const ldap_x_iovec iov[], int iovcnt, struct lextiof_socket_private *socketarg); + + +/* + * Structure for use with LBER_SOCKBUF_OPT_EXT_IO_FNS: + */ +struct lber_x_ext_io_fns { + /* lbextiofn_size should always be set to LBER_X_EXTIO_FNS_SIZE */ + int lbextiofn_size; + LDAP_X_EXTIOF_READ_CALLBACK *lbextiofn_read; + LDAP_X_EXTIOF_WRITE_CALLBACK *lbextiofn_write; + struct lextiof_socket_private *lbextiofn_socket_arg; + LDAP_X_EXTIOF_WRITEV_CALLBACK *lbextiofn_writev; +}; +#define LBER_X_EXTIO_FNS_SIZE sizeof(struct lber_x_ext_io_fns) + +/* + * liblber memory allocation callback functions. These are global to all + * Sockbufs and BerElements. Install your own functions by using a call + * like this: ber_set_option( NULL, LBER_OPT_MEMALLOC_FN_PTRS, &memalloc_fns ); + */ +typedef void * (LDAP_C LDAP_CALLBACK LDAP_MALLOC_CALLBACK)( size_t size ); +typedef void * (LDAP_C LDAP_CALLBACK LDAP_CALLOC_CALLBACK)( size_t nelem, + size_t elsize ); +typedef void * (LDAP_C LDAP_CALLBACK LDAP_REALLOC_CALLBACK)( void *ptr, + size_t size ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_FREE_CALLBACK)( void *ptr ); + +struct lber_memalloc_fns { + LDAP_MALLOC_CALLBACK *lbermem_malloc; + LDAP_CALLOC_CALLBACK *lbermem_calloc; + LDAP_REALLOC_CALLBACK *lbermem_realloc; + LDAP_FREE_CALLBACK *lbermem_free; +}; + +/* + * decode routines + */ +LDAP_API(ber_tag_t) LDAP_CALL ber_get_tag( BerElement *ber ); +LDAP_API(ber_tag_t) LDAP_CALL ber_skip_tag( BerElement *ber, + ber_len_t *len ); +LDAP_API(ber_tag_t) LDAP_CALL ber_peek_tag( BerElement *ber, + ber_len_t *len ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_int( BerElement *ber, ber_int_t *num ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_stringb( BerElement *ber, char *buf, + ber_len_t *len ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_stringa( BerElement *ber, + char **buf ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_stringal( BerElement *ber, + struct berval **bv ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_bitstringa( BerElement *ber, + char **buf, ber_len_t *len ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_null( BerElement *ber ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_boolean( BerElement *ber, + ber_int_t *boolval ); +LDAP_API(ber_tag_t) LDAP_CALL ber_first_element( BerElement *ber, + ber_len_t *len, char **last ); +LDAP_API(ber_tag_t) LDAP_CALL ber_next_element( BerElement *ber, + ber_len_t *len, char *last ); +LDAP_API(ber_tag_t) LDAP_C ber_scanf( BerElement *ber, const char *fmt, + ... ); +LDAP_API(void) LDAP_CALL ber_bvfree( struct berval *bv ); +LDAP_API(void) LDAP_CALL ber_bvecfree( struct berval **bv ); +LDAP_API(void) LDAP_CALL ber_svecfree( char **vals ); +LDAP_API(struct berval *) LDAP_CALL ber_bvdup( const struct berval *bv ); +LDAP_API(void) LDAP_CALL ber_set_string_translators( BerElement *ber, + BERTranslateProc encode_proc, BERTranslateProc decode_proc ); +LDAP_API(BerElement *) LDAP_CALL ber_init( const struct berval *bv ); + +/* + * encoding routines + */ +LDAP_API(int) LDAP_CALL ber_put_enum( BerElement *ber, ber_int_t num, + ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_int( BerElement *ber, ber_int_t num, + ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_ostring( BerElement *ber, char *str, + ber_len_t len, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_string( BerElement *ber, char *str, + ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_bitstring( BerElement *ber, char *str, + ber_len_t bitlen, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_null( BerElement *ber, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_boolean( BerElement *ber, + ber_int_t boolval, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_start_seq( BerElement *ber, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_start_set( BerElement *ber, ber_tag_t tag ); +LDAP_API(int) LDAP_CALL ber_put_seq( BerElement *ber ); +LDAP_API(int) LDAP_CALL ber_put_set( BerElement *ber ); +LDAP_API(int) LDAP_C ber_printf( BerElement *ber, const char *fmt, ... ); +LDAP_API(int) LDAP_CALL ber_flatten( BerElement *ber, + struct berval **bvPtr ); + +/* + * miscellaneous routines + */ +LDAP_API(void) LDAP_CALL ber_free( BerElement *ber, int freebuf ); +LDAP_API(void) LDAP_CALL ber_special_free(void* buf, BerElement *ber); +LDAP_API(int) LDAP_CALL ber_flush( Sockbuf *sb, BerElement *ber, int freeit ); +LDAP_API(BerElement*) LDAP_CALL ber_alloc( void ); +LDAP_API(BerElement*) LDAP_CALL der_alloc( void ); +LDAP_API(BerElement*) LDAP_CALL ber_alloc_t( int options ); +LDAP_API(void*) LDAP_CALL ber_special_alloc(size_t size, BerElement **ppBer); +LDAP_API(BerElement*) LDAP_CALL ber_dup( BerElement *ber ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_next( Sockbuf *sb, ber_len_t *len, + BerElement *ber ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_next_buffer( void *buffer, + size_t buffer_size, ber_len_t *len, BerElement *ber, + ber_len_t *Bytes_Scanned ); +LDAP_API(ber_tag_t) LDAP_CALL ber_get_next_buffer_ext( void *buffer, + size_t buffer_size, ber_len_t *len, BerElement *ber, + ber_len_t *Bytes_Scanned, Sockbuf *sb ); +LDAP_API(ber_int_t) LDAP_CALL ber_read( BerElement *ber, char *buf, + ber_len_t len ); +LDAP_API(ber_int_t) LDAP_CALL ber_write( BerElement *ber, char *buf, + ber_len_t len, int nosos ); +LDAP_API(void) LDAP_CALL ber_init_w_nullchar( BerElement *ber, int options ); +LDAP_API(void) LDAP_CALL ber_reset( BerElement *ber, int was_writing ); +LDAP_API(size_t) LDAP_CALL ber_get_buf_datalen( BerElement *ber ); +LDAP_API(int) LDAP_CALL ber_stack_init(BerElement *ber, int options, + char * buf, size_t size); +LDAP_API(char*) LDAP_CALL ber_get_buf_databegin (BerElement * ber); +LDAP_API(void) LDAP_CALL ber_sockbuf_free_data(Sockbuf *p); +LDAP_API(int) LDAP_CALL ber_set_option( BerElement *ber, int option, + void *value ); +LDAP_API(int) LDAP_CALL ber_get_option( BerElement *ber, int option, + void *value ); +LDAP_API(Sockbuf*) LDAP_CALL ber_sockbuf_alloc( void ); +LDAP_API(void) LDAP_CALL ber_sockbuf_free( Sockbuf* p ); +LDAP_API(int) LDAP_CALL ber_sockbuf_set_option( Sockbuf *sb, int option, + void *value ); +LDAP_API(int) LDAP_CALL ber_sockbuf_get_option( Sockbuf *sb, int option, + void *value ); + +#ifdef __cplusplus +} +#endif +#endif /* _LBER_H */ + diff --git a/ldap/c-sdk/include/lcache.h b/ldap/c-sdk/include/lcache.h new file mode 100644 index 0000000000..b070bd652f --- /dev/null +++ b/ldap/c-sdk/include/lcache.h @@ -0,0 +1,94 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* lcache.h - ldap persistent cache */ +#ifndef _LCACHE_H +#define _LCACHE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* calling conventions used by library */ +#ifndef LDAP_CALL +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_C __cdecl +#ifndef _WIN32 +#define __stdcall _far _pascal +#define LDAP_CALLBACK _loadds +#else +#define LDAP_CALLBACK +#endif /* _WIN32 */ +#define LDAP_PASCAL __stdcall +#define LDAP_CALL LDAP_PASCAL +#else /* _WINDOWS */ +#define LDAP_C +#define LDAP_CALLBACK +#define LDAP_PASCAL +#define LDAP_CALL +#endif /* _WINDOWS */ +#endif /* LDAP_CALL */ + +LDAP_API(int) LDAP_C lcache_init( LDAP *ld, void *arg ); +LDAP_API(int) LDAP_C lcache_bind( LDAP *ld, int msgid, unsigned long tag, + const char *dn, struct berval *cred, int method ); +LDAP_API(int) LDAP_C lcache_unbind( LDAP *ld, int msgid, unsigned long tag ); +LDAP_API(int) LDAP_C lcache_search( LDAP *ld, int msgid, unsigned long tag, + const char *dn, int scope, const char *filter, char **attrs, + int attrsonly ); +LDAP_API(int) LDAP_C lcache_compare( LDAP *ld, int msgid, unsigned long tag, + const char *dn, const char *attr, struct berval *val ); +LDAP_API(int) LDAP_C lcache_add( LDAP *ld, int msgid, unsigned long tag, + const char *dn, LDAPMod **entry ); +LDAP_API(int) LDAP_C lcache_delete( LDAP *ld, int msgid, unsigned long tag, + const char *dn ); +LDAP_API(int) LDAP_C lcache_rename( LDAP *ld, int msgid, unsigned long tag, + const char *dn, const char *newrdn, const char *newparent, + int deleteoldrdn ); +LDAP_API(int) LDAP_C lcache_modify( LDAP *ld, int msgid, unsigned long tag, + const char *dn, LDAPMod **mods ); +LDAP_API(int) LDAP_C lcache_modrdn( LDAP *ld, int msgid, unsigned long tag, + const char *dn, const char *newrdn, int deleteoldrdn ); +LDAP_API(int) LDAP_C lcache_result( LDAP *ld, int msgid, int all, + struct timeval *timeout, LDAPMessage **result ); +LDAP_API(int) LDAP_C lcache_flush( LDAP *ld, char *dn, char *filter ); + +#ifdef __cplusplus +} +#endif + +#endif /* _LCACHE_H */ diff --git a/ldap/c-sdk/include/ldap-deprecated.h b/ldap/c-sdk/include/ldap-deprecated.h new file mode 100644 index 0000000000..4b219844e4 --- /dev/null +++ b/ldap/c-sdk/include/ldap-deprecated.h @@ -0,0 +1,201 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap-deprecated.h - deprecated functions and declarations + * + * A deprecated API is an API that we recommend you no longer use, + * due to improvements in the LDAP C SDK. While deprecated APIs are + * currently still implemented, they may be removed in future + * implementations, and we recommend using other APIs. + * + * This file contain functions and declarations which have + * outlived their usefullness and have been deprecated. In many + * cases functions and declarations have been replaced with newer + * extended functions. In no way should applications rely on the + * declarations and defines within this files as they can and will + * disappear without any notice. + */ + +#ifndef _LDAP_DEPRECATED_H +#define _LDAP_DEPRECATED_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * establish an ldap session + */ +LDAP_API(LDAP *) LDAP_CALL ldap_open( const char *host, int port ); + +/* + * Authentication methods: + */ +#define LDAP_AUTH_NONE 0x00L +#define LDAP_AUTH_SIMPLE 0x80L +#define LDAP_AUTH_SASL 0xa3L +LDAP_API(int) LDAP_CALL ldap_bind( LDAP *ld, const char *who, + const char *passwd, int authmethod ); +LDAP_API(int) LDAP_CALL ldap_bind_s( LDAP *ld, const char *who, + const char *cred, int method ); + +LDAP_API(int) LDAP_CALL ldap_modrdn( LDAP *ld, const char *dn, + const char *newrdn ); +LDAP_API(int) LDAP_CALL ldap_modrdn_s( LDAP *ld, const char *dn, + const char *newrdn ); +LDAP_API(int) LDAP_CALL ldap_modrdn2( LDAP *ld, const char *dn, + const char *newrdn, int deleteoldrdn ); +LDAP_API(int) LDAP_CALL ldap_modrdn2_s( LDAP *ld, const char *dn, + const char *newrdn, int deleteoldrdn); + +LDAP_API(void) LDAP_CALL ldap_perror( LDAP *ld, const char *s ); +LDAP_API(int) LDAP_CALL ldap_result2error( LDAP *ld, LDAPMessage *r, + int freeit ); + +/* + * Preferred language and get_lang_values (an API extension -- + * LDAP_API_FEATURE_X_GETLANGVALUES) + * + * The following two APIs are deprecated + */ + +#define LDAP_OPT_PREFERRED_LANGUAGE 0x14 /* 20 - API extension */ +LDAP_API(char **) LDAP_CALL ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, + const char *target, char **type ); +LDAP_API(struct berval **) LDAP_CALL ldap_get_lang_values_len( LDAP *ld, + LDAPMessage *entry, const char *target, char **type ); + +/* + * Asynchronous I/O (an API extension). + */ +/* + * This option enables completely asynchronous IO. It works by using ioctl() + * on the fd, (or tlook()) + */ +#define LDAP_OPT_ASYNC_CONNECT 0x63 /* 99 - API extension */ + +/* + * functions and definitions that have been replaced by new improved ones + */ +/* + * Use ldap_get_option() with LDAP_OPT_API_INFO and an LDAPAPIInfo structure + * instead of ldap_version(). + */ +typedef struct _LDAPVersion { + int sdk_version; /* Version of the SDK, * 100 */ + int protocol_version; /* Highest protocol version supported, * 100 */ + int SSL_version; /* SSL version if this SDK supports it, * 100 */ + int security_level; /* highest level available */ + int reserved[4]; +} LDAPVersion; +#define LDAP_SECURITY_NONE 0 +LDAP_API(int) LDAP_CALL ldap_version( LDAPVersion *ver ); + +/* use ldap_create_filter() instead of ldap_build_filter() */ +LDAP_API(void) LDAP_CALL ldap_build_filter( char *buf, unsigned long buflen, + char *pattern, char *prefix, char *suffix, char *attr, + char *value, char **valwords ); +/* use ldap_set_filter_additions() instead of ldap_setfilteraffixes() */ +LDAP_API(void) LDAP_CALL ldap_setfilteraffixes( LDAPFiltDesc *lfdp, + char *prefix, char *suffix ); + +/* older result types a server can return -- use LDAP_RES_MODDN instead */ +#define LDAP_RES_MODRDN LDAP_RES_MODDN +#define LDAP_RES_RENAME LDAP_RES_MODDN + +/* older error messages */ +#define LDAP_AUTH_METHOD_NOT_SUPPORTED LDAP_STRONG_AUTH_NOT_SUPPORTED + +/* + * Generalized cache callback interface: + */ +#define LDAP_OPT_CACHE_FN_PTRS 0x0D /* 13 - API extension */ +#define LDAP_OPT_CACHE_STRATEGY 0x0E /* 14 - API extension */ +#define LDAP_OPT_CACHE_ENABLE 0x0F /* 15 - API extension */ + +/* cache strategies */ +#define LDAP_CACHE_CHECK 0 +#define LDAP_CACHE_POPULATE 1 +#define LDAP_CACHE_LOCALDB 2 + +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_BIND_CALLBACK)( LDAP *ld, int msgid, + unsigned long tag, const char *dn, const struct berval *creds, + int method); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_UNBIND_CALLBACK)( LDAP *ld, + int unused0, unsigned long unused1 ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_SEARCH_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *base, int scope, + const char LDAP_CALLBACK *filter, char **attrs, int attrsonly ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_COMPARE_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *dn, const char *attr, + const struct berval *value ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_ADD_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *dn, LDAPMod **attrs ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_DELETE_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *dn ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_MODIFY_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *dn, LDAPMod **mods ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_MODRDN_CALLBACK)( LDAP *ld, + int msgid, unsigned long tag, const char *dn, const char *newrdn, + int deleteoldrdn ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_RESULT_CALLBACK)( LDAP *ld, + int msgid, int all, struct timeval *timeout, LDAPMessage **result ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_CF_FLUSH_CALLBACK)( LDAP *ld, + const char *dn, const char *filter ); + +struct ldap_cache_fns { + void *lcf_private; + LDAP_CF_BIND_CALLBACK *lcf_bind; + LDAP_CF_UNBIND_CALLBACK *lcf_unbind; + LDAP_CF_SEARCH_CALLBACK *lcf_search; + LDAP_CF_COMPARE_CALLBACK *lcf_compare; + LDAP_CF_ADD_CALLBACK *lcf_add; + LDAP_CF_DELETE_CALLBACK *lcf_delete; + LDAP_CF_MODIFY_CALLBACK *lcf_modify; + LDAP_CF_MODRDN_CALLBACK *lcf_modrdn; + LDAP_CF_RESULT_CALLBACK *lcf_result; + LDAP_CF_FLUSH_CALLBACK *lcf_flush; +}; + +LDAP_API(int) LDAP_CALL ldap_cache_flush( LDAP *ld, const char *dn, + const char *filter ); + + +#ifdef __cplusplus +} +#endif +#endif /* _LDAP_DEPRECATED_H */ diff --git a/ldap/c-sdk/include/ldap-extension.h b/ldap/c-sdk/include/ldap-extension.h new file mode 100644 index 0000000000..7bbefdfb22 --- /dev/null +++ b/ldap/c-sdk/include/ldap-extension.h @@ -0,0 +1,853 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap-extension.h - extensions to the ldap c api specification */ + +#ifndef _LDAP_EXTENSION_H +#define _LDAP_EXTENSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LDAP_PORT_MAX 65535 /* API extension */ +#define LDAP_VERSION1 1 /* API extension */ +#define LDAP_VERSION LDAP_VERSION3 /* API extension */ + +/* + * C LDAP features we support that are not (yet) part of the LDAP C API + * Internet Draft. Use the ldap_get_option() call with an option value of + * LDAP_OPT_API_FEATURE_INFO to retrieve information about a feature. + * + * Note that this list is incomplete; it includes only the most widely + * used extensions. Also, the version is 1 for all of these for now. + */ +#define LDAP_API_FEATURE_SERVER_SIDE_SORT 1 +#define LDAP_API_FEATURE_VIRTUAL_LIST_VIEW 1 +#define LDAP_API_FEATURE_PERSISTENT_SEARCH 1 +#define LDAP_API_FEATURE_PROXY_AUTHORIZATION 1 +#define LDAP_API_FEATURE_X_LDERRNO 1 +#define LDAP_API_FEATURE_X_MEMCACHE 1 +#define LDAP_API_FEATURE_X_IO_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_EXTIO_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_DNS_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_MEMALLOC_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_THREAD_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_EXTHREAD_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_GETLANGVALUES 1 +#define LDAP_API_FEATURE_X_CLIENT_SIDE_SORT 1 +#define LDAP_API_FEATURE_X_URL_FUNCTIONS 1 +#define LDAP_API_FEATURE_X_FILTER_FUNCTIONS 1 + +#define LDAP_ROOT_DSE "" /* API extension */ + +#define LDAP_OPT_DESC 0x01 /* 1 */ + +#define NULLMSG ((LDAPMessage *)0) + +/*built-in SASL methods */ +#define LDAP_SASL_EXTERNAL "EXTERNAL" /* TLS/SSL extension */ + +/* possible error codes we can be returned */ +#define LDAP_PARTIAL_RESULTS 0x09 /* 9 (UMich LDAPv2 extn) */ +#define NAME_ERROR(n) ((n & 0xf0) == 0x20) + +#define LDAP_SORT_CONTROL_MISSING 0x3C /* 60 (server side sort extn) */ +#define LDAP_INDEX_RANGE_ERROR 0x3D /* 61 (VLV extn) */ + +/* + * LDAPv3 server controls we know about + */ +#define LDAP_CONTROL_MANAGEDSAIT "2.16.840.1.113730.3.4.2" +#define LDAP_CONTROL_SORTREQUEST "1.2.840.113556.1.4.473" +#define LDAP_CONTROL_SORTRESPONSE "1.2.840.113556.1.4.474" +#define LDAP_CONTROL_PERSISTENTSEARCH "2.16.840.1.113730.3.4.3" +#define LDAP_CONTROL_ENTRYCHANGE "2.16.840.1.113730.3.4.7" +#define LDAP_CONTROL_VLVREQUEST "2.16.840.1.113730.3.4.9" +#define LDAP_CONTROL_VLVRESPONSE "2.16.840.1.113730.3.4.10" +#define LDAP_CONTROL_PROXYAUTH "2.16.840.1.113730.3.4.12" /* version 1 +*/ +#define LDAP_CONTROL_PROXIEDAUTH "2.16.840.1.113730.3.4.18" /* version 2 +*/ + +/* Authorization Identity Request and Response Controls */ +#define LDAP_CONTROL_AUTHZID_REQ "2.16.840.1.113730.3.4.16" +#define LDAP_CONTROL_AUTHZID_RES "2.16.840.1.113730.3.4.15" + +/* Authentication request and response controls */ +#define LDAP_CONTROL_AUTH_REQUEST LDAP_CONTROL_AUTHZID_REQ +#define LDAP_CONTROL_AUTH_RESPONSE LDAP_CONTROL_AUTHZID_RES + +/* Password information sent back to client */ +#define LDAP_CONTROL_PWEXPIRED "2.16.840.1.113730.3.4.4" +#define LDAP_CONTROL_PWEXPIRING "2.16.840.1.113730.3.4.5" + +/* Password Policy Control */ +#define LDAP_CONTROL_PASSWD_POLICY "1.3.6.1.4.1.42.2.27.8.5.1" + +/* Password Policy Control compatibility macros */ +#define LDAP_X_CONTROL_PWPOLICY_REQUEST LDAP_CONTROL_PASSWD_POLICY +#define LDAP_X_CONTROL_PWPOLICY_RESPONSE LDAP_CONTROL_PASSWD_POLICY +#define LDAP_CONTROL_PASSWORDPOLICYREQUEST LDAP_CONTROL_PASSWD_POLICY +#define LDAP_CONTROL_PASSWORDPOLICYRESPONSE LDAP_CONTROL_PASSWD_POLICY + +/* Password Modify Extended Operation */ +#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1" + +/* Suppress virtual/inherited attribute values */ +#define LDAP_CONTROL_REAL_ATTRS_ONLY "2.16.840.1.113730.3.4.17" + +/* Only return virtual/inherited attribute values */ +#define LDAP_CONTROL_VIRTUAL_ATTRS_ONLY "2.16.840.1.113730.3.4.19" + +/* getEffectiveRights request */ +#define LDAP_CONTROL_GETEFFECTIVERIGHTS_REQUEST "1.3.6.1.4.1.42.2.27.9.5.2" + +/* Password Policy Control to get account availability */ +#define LDAP_CONTROL_ACCOUNT_USABLE "1.3.6.1.4.1.42.2.27.9.5.8" + +/* "Who am I?" Extended Operation */ +#define LDAP_EXOP_WHO_AM_I "1.3.6.1.4.1.4203.1.11.3" + +LDAP_API(void) LDAP_CALL ldap_ber_free( BerElement *ber, int freebuf ); + +LDAP_API(LDAPControl *) LDAP_CALL ldap_find_control( const char *oid, + LDAPControl **ctrls ); + +/* + * Server side sorting of search results (an LDAPv3 extension -- + * LDAP_API_FEATURE_SERVER_SIDE_SORT) + */ +typedef struct LDAPsortkey { /* structure for a sort-key */ + char * sk_attrtype; + char * sk_matchruleoid; + int sk_reverseorder; +} LDAPsortkey; + +/* where LDAP_CONTROL_ACCOUNT_USABLE control parse results */ +typedef struct LDAPuserstatus { /* user account availability */ + unsigned int us_available; /* availability status */ +#define LDAP_US_ACCOUNT_USABLE 1 +#define LDAP_US_ACCOUNT_NOT_USABLE 0 + int us_expire; /* will expire in seconds */ + int us_inactive; /* boolean inactivation status */ +#define LDAP_US_ACCOUNT_ACTIVE 0 +#define LDAP_US_ACCOUNT_INACTIVE 1 + int us_reset; /* boolean password reset */ +#define LDAP_US_ACCOUNT_NOT_RESET 0 +#define LDAP_US_ACCOUNT_RESET 1 + int us_expired; /* boolean password expired */ +#define LDAP_US_ACCOUNT_NOT_EXPIRED 0 +#define LDAP_US_ACCOUNT_EXPIRED 1 + int us_remaining; /* remaining logins */ + int us_seconds; /* will unlock in seconds */ +} LDAPuserstatus; + +/* LDAP_CONTROL_PASSWD_POLICY results */ +typedef enum passpolicyerror_enum { + PP_passwordExpired = 0, + PP_accountLocked = 1, + PP_changeAfterReset = 2, + PP_passwordModNotAllowed = 3, + PP_mustSupplyOldPassword = 4, + PP_insufficientPasswordQuality = 5, + PP_passwordTooShort = 6, + PP_passwordTooYoung = 7, + PP_passwordInHistory = 8, + PP_noError = 65535 +} LDAPPasswordPolicyError; + +LDAP_API(int) LDAP_CALL ldap_create_sort_control( LDAP *ld, + LDAPsortkey **sortKeyList, const char ctl_iscritical, + LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_parse_sort_control( LDAP *ld, + LDAPControl **ctrls, ber_int_t *result, char **attribute ); + +LDAP_API(void) LDAP_CALL ldap_free_sort_keylist( LDAPsortkey **sortKeyList ); +LDAP_API(int) LDAP_CALL ldap_create_sort_keylist( LDAPsortkey ***sortKeyList, + const char *string_rep ); + +LDAP_API(int) LDAP_CALL ldap_create_userstatus_control( + LDAP *ld, const char ctl_iscritical, LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_parse_userstatus_control( LDAP *ld, + LDAPControl **ctrlp, LDAPuserstatus *us ); + +LDAP_API(int) LDAP_CALL ldap_create_passwordpolicy_control( LDAP *ld, + LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_create_passwordpolicy_control_ext( LDAP *ld, + const char ctl_iscritical, LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_parse_passwordpolicy_control( LDAP *ld, + LDAPControl *ctrlp, ber_int_t *expirep, ber_int_t *gracep, + LDAPPasswordPolicyError *errorp ); +LDAP_API(int) LDAP_CALL ldap_parse_passwordpolicy_control_ext ( LDAP *ld, + LDAPControl **ctrlp, ber_int_t *expirep, ber_int_t *gracep, + LDAPPasswordPolicyError *errorp ); +LDAP_API(const char *) LDAP_CALL ldap_passwordpolicy_err2txt( + LDAPPasswordPolicyError err ); + +LDAP_API(int) LDAP_CALL ldap_create_authzid_control( LDAP *ld, + const char ctl_iscritical, LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_parse_authzid_control( LDAP *ld, + LDAPControl **ctrlp, char **authzid ); + +LDAP_API(int) LDAP_CALL ldap_whoami( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_whoami_s( LDAP *ld, struct berval **authzid, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_parse_whoami( LDAP *ld, LDAPMessage *result, + struct berval **authzid ); + +LDAP_API(int) LDAP_CALL ldap_create_geteffectiveRights_control( LDAP *ld, + const char *authzid, const char **attrlist, const char ctl_iscritical, + LDAPControl **ctrlp ); + +/* + * Virtual list view (an LDAPv3 extension -- LDAP_API_FEATURE_VIRTUAL_LIST_VIEW) + */ +/* + * structure that describes a VirtualListViewRequest control. + * note that ldvlist_index and ldvlist_size are only relevant to + * ldap_create_virtuallist_control() if ldvlist_attrvalue is NULL. + */ +typedef struct ldapvirtuallist { + ber_int_t ldvlist_before_count; /* # entries before target */ + ber_int_t ldvlist_after_count; /* # entries after target */ + char *ldvlist_attrvalue; /* jump to this value */ + ber_int_t ldvlist_index; /* list offset */ + ber_int_t ldvlist_size; /* number of items in vlist */ + void *ldvlist_extradata; /* for use by application */ +} LDAPVirtualList; + +/* + * VLV functions: + */ +LDAP_API(int) LDAP_CALL ldap_create_virtuallist_control( LDAP *ld, + LDAPVirtualList *ldvlistp, LDAPControl **ctrlp ); + +LDAP_API(int) LDAP_CALL ldap_parse_virtuallist_control( LDAP *ld, + LDAPControl **ctrls, ber_int_t *target_posp, + ber_int_t *list_sizep, int *errcodep ); + +/* + * Routines for creating persistent search controls and for handling + * "entry changed notification" controls (an LDAPv3 extension -- + * LDAP_API_FEATURE_PERSISTENT_SEARCH) + */ +#define LDAP_CHANGETYPE_ADD 1 +#define LDAP_CHANGETYPE_DELETE 2 +#define LDAP_CHANGETYPE_MODIFY 4 +#define LDAP_CHANGETYPE_MODDN 8 +#define LDAP_CHANGETYPE_ANY (1|2|4|8) +LDAP_API(int) LDAP_CALL ldap_create_persistentsearch_control( LDAP *ld, + int changetypes, int changesonly, int return_echg_ctls, + char ctl_iscritical, LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_parse_entrychange_control( LDAP *ld, + LDAPControl **ctrls, ber_int_t *chgtypep, char **prevdnp, + int *chgnumpresentp, ber_int_t *chgnump ); + +/* + * Routines for creating Proxied Authorization controls (an LDAPv3 + * extension -- LDAP_API_FEATURE_PROXY_AUTHORIZATION) + * ldap_create_proxyauth_control() is for the old (version 1) control. + * ldap_create_proxiedauth_control() is for the newer (version 2) control. + */ +LDAP_API(int) LDAP_CALL ldap_create_proxyauth_control( LDAP *ld, + const char *dn, const char ctl_iscritical, LDAPControl **ctrlp ); +LDAP_API(int) LDAP_CALL ldap_create_proxiedauth_control( LDAP *ld, + const char *authzid, LDAPControl **ctrlp ); + +/* + * Functions to get and set LDAP error information (API extension -- + * LDAP_API_FEATURE_X_LDERRNO ) + * + * By using LDAP_OPT_THREAD_FN_PTRS, you can arrange for the error info. to + * be thread-specific. + */ +LDAP_API(int) LDAP_CALL ldap_get_lderrno( LDAP *ld, char **m, char **s ); +LDAP_API(int) LDAP_CALL ldap_set_lderrno( LDAP *ld, int e, char *m, char *s ); + + +/* + * LDAP URL functions and definitions (an API extension -- + * LDAP_API_FEATURE_X_URL_FUNCTIONS) + */ +/* + * types for ldap URL handling + */ +typedef struct ldap_url_desc { + char *lud_host; + int lud_port; + char *lud_dn; + char **lud_attrs; + int lud_scope; + char *lud_filter; + unsigned long lud_options; +#define LDAP_URL_OPT_SECURE 0x01 + char *lud_string; /* for internal use only */ +} LDAPURLDesc; + +#define NULLLDAPURLDESC ((LDAPURLDesc *)NULL) + +/* + * possible errors returned by ldap_url_parse() + */ +#define LDAP_URL_ERR_NOTLDAP 1 /* URL doesn't begin with "ldap://" */ +#define LDAP_URL_ERR_NODN 2 /* URL has no DN (required) */ +#define LDAP_URL_ERR_BADSCOPE 3 /* URL scope string is invalid */ +#define LDAP_URL_ERR_MEM 4 /* can't allocate memory space */ +#define LDAP_URL_ERR_PARAM 5 /* bad parameter to an URL function */ +#define LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION 6 + +/* + * URL functions: + */ +LDAP_API(int) LDAP_CALL ldap_is_ldap_url( const char *url ); +LDAP_API(int) LDAP_CALL ldap_url_parse( const char *url, LDAPURLDesc **ludpp ); +LDAP_API(int) LDAP_CALL ldap_url_parse_no_defaults( const char *url, + LDAPURLDesc **ludpp, int dn_required); +LDAP_API(void) LDAP_CALL ldap_free_urldesc( LDAPURLDesc *ludp ); +LDAP_API(int) LDAP_CALL ldap_url_search( LDAP *ld, const char *url, + int attrsonly ); +LDAP_API(int) LDAP_CALL ldap_url_search_s( LDAP *ld, const char *url, + int attrsonly, LDAPMessage **res ); +LDAP_API(int) LDAP_CALL ldap_url_search_st( LDAP *ld, const char *url, + int attrsonly, struct timeval *timeout, LDAPMessage **res ); + + +/* + * Function to dispose of an array of LDAPMod structures (an API extension). + * Warning: don't use this unless the mods array was allocated using the + * same memory allocator as is being used by libldap. + */ +LDAP_API(void) LDAP_CALL ldap_mods_free( LDAPMod **mods, int freemods ); + +/* + * SSL option (an API extension): + */ +#define LDAP_OPT_SSL 0x0A /* 10 - API extension */ + +/* + * Referral hop limit (an API extension): + */ +#define LDAP_OPT_REFERRAL_HOP_LIMIT 0x10 /* 16 - API extension */ + +/* + * Rebind callback function (an API extension) + */ +#define LDAP_OPT_REBIND_FN 0x06 /* 6 - API extension */ +#define LDAP_OPT_REBIND_ARG 0x07 /* 7 - API extension */ +typedef int (LDAP_CALL LDAP_CALLBACK LDAP_REBINDPROC_CALLBACK)( LDAP *ld, + char **dnp, char **passwdp, int *authmethodp, int freeit, void *arg); +LDAP_API(void) LDAP_CALL ldap_set_rebind_proc( LDAP *ld, + LDAP_REBINDPROC_CALLBACK *rebindproc, void *arg ); + +/* + * Thread function callbacks (an API extension -- + * LDAP_API_FEATURE_X_THREAD_FUNCTIONS). + */ +#define LDAP_OPT_THREAD_FN_PTRS 0x05 /* 5 - API extension */ + +/* + * Thread callback functions: + */ +typedef void *(LDAP_C LDAP_CALLBACK LDAP_TF_MUTEX_ALLOC_CALLBACK)( void ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_TF_MUTEX_FREE_CALLBACK)( void *m ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_MUTEX_LOCK_CALLBACK)( void *m ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_MUTEX_UNLOCK_CALLBACK)( void *m ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_GET_ERRNO_CALLBACK)( void ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_TF_SET_ERRNO_CALLBACK)( int e ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_GET_LDERRNO_CALLBACK)( + char **matchedp, char **errmsgp, void *arg ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_TF_SET_LDERRNO_CALLBACK)( int err, + char *matched, char *errmsg, void *arg ); + +/* + * Structure to hold thread function pointers: + */ +struct ldap_thread_fns { + LDAP_TF_MUTEX_ALLOC_CALLBACK *ltf_mutex_alloc; + LDAP_TF_MUTEX_FREE_CALLBACK *ltf_mutex_free; + LDAP_TF_MUTEX_LOCK_CALLBACK *ltf_mutex_lock; + LDAP_TF_MUTEX_UNLOCK_CALLBACK *ltf_mutex_unlock; + LDAP_TF_GET_ERRNO_CALLBACK *ltf_get_errno; + LDAP_TF_SET_ERRNO_CALLBACK *ltf_set_errno; + LDAP_TF_GET_LDERRNO_CALLBACK *ltf_get_lderrno; + LDAP_TF_SET_LDERRNO_CALLBACK *ltf_set_lderrno; + void *ltf_lderrno_arg; +}; + +/* + * Extended I/O function callbacks option (an API extension -- + * LDAP_API_FEATURE_X_EXTIO_FUNCTIONS). + */ +#define LDAP_X_OPT_EXTIO_FN_PTRS (LDAP_OPT_PRIVATE_EXTENSION_BASE + 0x0F00) + /* 0x4000 + 0x0F00 = 0x4F00 = 20224 - API extension */ + +/* Additional Extended I/O function callback option (for Extended Socket Arg callback) */ +#define LDAP_X_OPT_SOCKETARG (LDAP_OPT_PRIVATE_EXTENSION_BASE + 0x0F02) + /* 0x4000 + 0x0F02 = 0x4F02 = 20226 - API extension */ + +/* + * These extended I/O function callbacks echo the BSD socket API but accept + * an extra pointer parameter at the end of their argument list that can + * be used by client applications for their own needs. For some of the calls, + * the pointer is a session argument of type struct lextiof_session_private * + * that is associated with the LDAP session handle (LDAP *). For others, the + * pointer is a socket specific struct lextiof_socket_private * argument that + * is associated with a particular socket (a TCP connection). + * + * The lextiof_session_private and lextiof_socket_private structures are not + * defined by the LDAP C API; users of this extended I/O interface should + * define these themselves. + * + * The combination of the integer socket number (i.e., lpoll_fd, which is + * the value returned by the CONNECT callback) and the application specific + * socket argument (i.e., lpoll_socketarg, which is the value set in *sockargpp + * by the CONNECT callback) must be unique. + * + * The types for the extended READ and WRITE callbacks are actually in lber.h. + * + * The CONNECT callback gets passed both the session argument (sessionarg) + * and a pointer to a socket argument (socketargp) so it has the + * opportunity to set the socket-specific argument. The CONNECT callback + * also takes a timeout parameter whose value can be set by calling + * ldap_set_option( ld, LDAP_X_OPT_..., &val ). The units used for the + * timeout parameter are milliseconds. + * + * A POLL interface is provided instead of a select() one. The timeout is + * in milliseconds. + * + * A NEWHANDLE callback function is also provided. It is called right + * after the LDAP session handle is created, e.g., during ldap_init(). + * If the NEWHANDLE callback returns anything other than LDAP_SUCCESS, + * the session handle allocation fails. + * + * A DISPOSEHANDLE callback function is also provided. It is called right + * before the LDAP session handle and its contents are destroyed, e.g., + * during ldap_unbind(). + */ + +/* + * Special timeout values for poll and connect: + */ +#define LDAP_X_IO_TIMEOUT_NO_WAIT 0 /* return immediately */ +#define LDAP_X_IO_TIMEOUT_NO_TIMEOUT (-1) /* block indefinitely */ + +/* LDAP poll()-like descriptor: + */ +typedef struct ldap_x_pollfd { /* used by LDAP_X_EXTIOF_POLL_CALLBACK */ + int lpoll_fd; /* integer file descriptor / socket */ + struct lextiof_socket_private + *lpoll_socketarg; + /* pointer socket and for use by */ + /* application */ + short lpoll_events; /* requested event */ + short lpoll_revents; /* returned event */ +} LDAP_X_PollFD; + +/* Event flags for lpoll_events and lpoll_revents: + */ +#define LDAP_X_POLLIN 0x01 /* regular data ready for reading */ +#define LDAP_X_POLLPRI 0x02 /* high priority data available */ +#define LDAP_X_POLLOUT 0x04 /* ready for writing */ +#define LDAP_X_POLLERR 0x08 /* error occurred -- only in lpoll_revents */ +#define LDAP_X_POLLHUP 0x10 /* connection closed -- only in lpoll_revents */ +#define LDAP_X_POLLNVAL 0x20 /* invalid lpoll_fd -- only in lpoll_revents */ + +/* Options passed to LDAP_X_EXTIOF_CONNECT_CALLBACK to modify socket behavior: + */ +#define LDAP_X_EXTIOF_OPT_NONBLOCKING 0x01 /* turn on non-blocking mode */ +#define LDAP_X_EXTIOF_OPT_SECURE 0x02 /* turn on 'secure' mode */ + + +/* extended I/O callback function prototypes: + */ +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_CONNECT_CALLBACK )( + const char *hostlist, int port, /* host byte order */ + int timeout /* milliseconds */, + unsigned long options, /* bitmapped options */ + struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_CLOSE_CALLBACK )( + int s, struct lextiof_socket_private *socketarg ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_POLL_CALLBACK)( + LDAP_X_PollFD fds[], int nfds, int timeout /* milliseconds */, + struct lextiof_session_private *sessionarg ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_NEWHANDLE_CALLBACK)( + LDAP *ld, struct lextiof_session_private *sessionarg ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK)( + LDAP *ld, struct lextiof_session_private *sessionarg ); + + +/* Structure to hold extended I/O function pointers: + */ +struct ldap_x_ext_io_fns { + /* lextiof_size should always be set to LDAP_X_EXTIO_FNS_SIZE */ + int lextiof_size; + LDAP_X_EXTIOF_CONNECT_CALLBACK *lextiof_connect; + LDAP_X_EXTIOF_CLOSE_CALLBACK *lextiof_close; + LDAP_X_EXTIOF_READ_CALLBACK *lextiof_read; + LDAP_X_EXTIOF_WRITE_CALLBACK *lextiof_write; + LDAP_X_EXTIOF_POLL_CALLBACK *lextiof_poll; + LDAP_X_EXTIOF_NEWHANDLE_CALLBACK *lextiof_newhandle; + LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lextiof_disposehandle; + void *lextiof_session_arg; + LDAP_X_EXTIOF_WRITEV_CALLBACK *lextiof_writev; +}; +#define LDAP_X_EXTIO_FNS_SIZE sizeof(struct ldap_x_ext_io_fns) + +/* + * Utility functions for parsing space-separated host lists (useful for + * implementing an extended I/O CONNECT callback function). + */ +struct ldap_x_hostlist_status; +LDAP_API(int) LDAP_CALL ldap_x_hostlist_first( const char *hostlist, + int defport, char **hostp, int *portp /* host byte order */, + struct ldap_x_hostlist_status **statusp ); +LDAP_API(int) LDAP_CALL ldap_x_hostlist_next( char **hostp, + int *portp /* host byte order */, struct ldap_x_hostlist_status *status +); +LDAP_API(void) LDAP_CALL ldap_x_hostlist_statusfree( + struct ldap_x_hostlist_status *status ); + +/* + * Client side sorting of entries (an API extension -- + * LDAP_API_FEATURE_X_CLIENT_SIDE_SORT) + */ +/* + * Client side sorting callback functions: + */ +typedef const struct berval* (LDAP_C LDAP_CALLBACK + LDAP_KEYGEN_CALLBACK)( void *arg, LDAP *ld, LDAPMessage *entry ); +typedef int (LDAP_C LDAP_CALLBACK + LDAP_KEYCMP_CALLBACK)( void *arg, const struct berval*, + const struct berval* ); +typedef void (LDAP_C LDAP_CALLBACK + LDAP_KEYFREE_CALLBACK)( void *arg, const struct berval* ); +typedef int (LDAP_C LDAP_CALLBACK + LDAP_CMP_CALLBACK)(const char *val1, const char *val2); +typedef int (LDAP_C LDAP_CALLBACK + LDAP_VALCMP_CALLBACK)(const char **val1p, const char **val2p); + +/* + * Client side sorting functions: + */ +LDAP_API(int) LDAP_CALL ldap_keysort_entries( LDAP *ld, LDAPMessage **chain, + void *arg, LDAP_KEYGEN_CALLBACK *gen, LDAP_KEYCMP_CALLBACK *cmp, + LDAP_KEYFREE_CALLBACK *fre ); +LDAP_API(int) LDAP_CALL ldap_multisort_entries( LDAP *ld, LDAPMessage **chain, + char **attr, LDAP_CMP_CALLBACK *cmp ); +LDAP_API(int) LDAP_CALL ldap_sort_entries( LDAP *ld, LDAPMessage **chain, + char *attr, LDAP_CMP_CALLBACK *cmp ); +LDAP_API(int) LDAP_CALL ldap_sort_values( LDAP *ld, char **vals, + LDAP_VALCMP_CALLBACK *cmp ); +LDAP_API(int) LDAP_C LDAP_CALLBACK ldap_sort_strcasecmp( const char **a, + const char **b ); + + +/* + * Filter functions and definitions (an API extension -- + * LDAP_API_FEATURE_X_FILTER_FUNCTIONS) + */ +/* + * Structures, constants, and types for filter utility routines: + */ +typedef struct ldap_filt_info { + char *lfi_filter; + char *lfi_desc; + int lfi_scope; /* LDAP_SCOPE_BASE, etc */ + int lfi_isexact; /* exact match filter? */ + struct ldap_filt_info *lfi_next; +} LDAPFiltInfo; + +#define LDAP_FILT_MAXSIZ 1024 + +typedef struct ldap_filt_list LDAPFiltList; /* opaque filter list handle */ +typedef struct ldap_filt_desc LDAPFiltDesc; /* opaque filter desc handle */ + +/* + * Filter utility functions: + */ +LDAP_API(LDAPFiltDesc *) LDAP_CALL ldap_init_getfilter( char *fname ); +LDAP_API(LDAPFiltDesc *) LDAP_CALL ldap_init_getfilter_buf( char *buf, + long buflen ); +LDAP_API(LDAPFiltInfo *) LDAP_CALL ldap_getfirstfilter( LDAPFiltDesc *lfdp, + char *tagpat, char *value ); +LDAP_API(LDAPFiltInfo *) LDAP_CALL ldap_getnextfilter( LDAPFiltDesc *lfdp ); +LDAP_API(int) LDAP_CALL ldap_set_filter_additions( LDAPFiltDesc *lfdp, + char *prefix, char *suffix ); +LDAP_API(int) LDAP_CALL ldap_create_filter( char *buf, unsigned long buflen, + char *pattern, char *prefix, char *suffix, char *attr, + char *value, char **valwords ); +LDAP_API(void) LDAP_CALL ldap_getfilter_free( LDAPFiltDesc *lfdp ); + +/* + * Friendly mapping structure and routines (an API extension) + */ +typedef struct friendly { + char *f_unfriendly; + char *f_friendly; +} *FriendlyMap; +LDAP_API(char *) LDAP_CALL ldap_friendly_name( char *filename, char *name, + FriendlyMap *map ); +LDAP_API(void) LDAP_CALL ldap_free_friendlymap( FriendlyMap *map ); + +/* + * In Memory Cache (an API extension -- LDAP_API_FEATURE_X_MEMCACHE) + */ +typedef struct ldapmemcache LDAPMemCache; /* opaque in-memory cache handle */ + +LDAP_API(int) LDAP_CALL ldap_memcache_init( unsigned long ttl, + unsigned long size, char **baseDNs, struct ldap_thread_fns *thread_fns, + LDAPMemCache **cachep ); +LDAP_API(int) LDAP_CALL ldap_memcache_set( LDAP *ld, LDAPMemCache *cache ); +LDAP_API(int) LDAP_CALL ldap_memcache_get( LDAP *ld, LDAPMemCache **cachep ); +LDAP_API(void) LDAP_CALL ldap_memcache_flush( LDAPMemCache *cache, char *dn, + int scope ); +LDAP_API(void) LDAP_CALL ldap_memcache_flush_results( LDAPMemCache *cache, + char *dn, int scope ); +LDAP_API(void) LDAP_CALL ldap_memcache_destroy( LDAPMemCache *cache ); +LDAP_API(void) LDAP_CALL ldap_memcache_update( LDAPMemCache *cache ); + +/* + * Timeout value for nonblocking connect call + */ +#define LDAP_X_OPT_CONNECT_TIMEOUT (LDAP_OPT_PRIVATE_EXTENSION_BASE + 0x0F01) + /* 0x4000 + 0x0F01 = 0x4F01 = 20225 - API extension */ + +/* + * Socket buffer structure associated to the LDAP connection + */ +#define LDAP_X_OPT_SOCKBUF (LDAP_OPT_PRIVATE_EXTENSION_BASE + 0x0F03) + /* 0x4000 + 0x0F03 = 0x4F03 = 20227 - API extension */ + +/* + * Memory allocation callback functions (an API extension -- + * LDAP_API_FEATURE_X_MEMALLOC_FUNCTIONS). These are global and can + * not be set on a per-LDAP session handle basis. Install your own + * functions by making a call like this: + * ldap_set_option( NULL, LDAP_OPT_MEMALLOC_FN_PTRS, &memalloc_fns ); + * + * look in lber.h for the function typedefs themselves. + */ +#define LDAP_OPT_MEMALLOC_FN_PTRS 0x61 /* 97 - API extension */ + +struct ldap_memalloc_fns { + LDAP_MALLOC_CALLBACK *ldapmem_malloc; + LDAP_CALLOC_CALLBACK *ldapmem_calloc; + LDAP_REALLOC_CALLBACK *ldapmem_realloc; + LDAP_FREE_CALLBACK *ldapmem_free; +}; + + +/* + * Memory allocation functions (an API extension) + */ +void *ldap_x_malloc( size_t size ); +void *ldap_x_calloc( size_t nelem, size_t elsize ); +void *ldap_x_realloc( void *ptr, size_t size ); +void ldap_x_free( void *ptr ); + +/* + * Server reconnect (an API extension). + */ +#define LDAP_OPT_RECONNECT 0x62 /* 98 - API extension */ + + +/* + * Extra thread callback functions (an API extension -- + * LDAP_API_FEATURE_X_EXTHREAD_FUNCTIONS) + */ +#define LDAP_OPT_EXTRA_THREAD_FN_PTRS 0x65 /* 101 - API extension */ + +/* + * When bind is called, don't bind if there's a connection open with the same DN + */ +#define LDAP_OPT_NOREBIND 0x66 /* 102 - API extension */ + +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_MUTEX_TRYLOCK_CALLBACK)( void *m ); +typedef void *(LDAP_C LDAP_CALLBACK LDAP_TF_SEMA_ALLOC_CALLBACK)( void ); +typedef void (LDAP_C LDAP_CALLBACK LDAP_TF_SEMA_FREE_CALLBACK)( void *s ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_SEMA_WAIT_CALLBACK)( void *s ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_TF_SEMA_POST_CALLBACK)( void *s ); +typedef void *(LDAP_C LDAP_CALLBACK LDAP_TF_THREADID_CALLBACK)(void); + +struct ldap_extra_thread_fns { + LDAP_TF_MUTEX_TRYLOCK_CALLBACK *ltf_mutex_trylock; + LDAP_TF_SEMA_ALLOC_CALLBACK *ltf_sema_alloc; + LDAP_TF_SEMA_FREE_CALLBACK *ltf_sema_free; + LDAP_TF_SEMA_WAIT_CALLBACK *ltf_sema_wait; + LDAP_TF_SEMA_POST_CALLBACK *ltf_sema_post; + LDAP_TF_THREADID_CALLBACK *ltf_threadid_fn; +}; + +/* + * Debugging level (an API extension) + */ +#define LDAP_OPT_DEBUG_LEVEL 0x6E /* 110 - API extension */ +/* On UNIX, there's only one copy of ldap_debug */ +/* On NT, each dll keeps its own module_ldap_debug, which */ +/* points to the process' ldap_debug and needs initializing after load */ +#ifdef _WIN32 +extern int *module_ldap_debug; +typedef void (*set_debug_level_fn_t)(int*); +#endif + +#ifdef LDAP_DNS +#define LDAP_OPT_DNS 0x0C /* 12 - API extension */ +#endif + +/* + * UTF-8 routines (should these move into libnls?) + */ +/* number of bytes in character */ +LDAP_API(int) LDAP_CALL ldap_utf8len( const char* ); +/* find next character */ +LDAP_API(char*) LDAP_CALL ldap_utf8next( char* ); +/* find previous character */ +LDAP_API(char*) LDAP_CALL ldap_utf8prev( char* ); +/* copy one character */ +LDAP_API(int) LDAP_CALL ldap_utf8copy( char* dst, const char* src ); +/* total number of characters */ +LDAP_API(size_t) LDAP_CALL ldap_utf8characters( const char* ); +/* get one UCS-4 character, and move *src to the next character */ +LDAP_API(unsigned long) LDAP_CALL ldap_utf8getcc( const char** src ); +/* UTF-8 aware strtok_r() */ +LDAP_API(char*) LDAP_CALL ldap_utf8strtok_r( char* src, const char* brk, char** +next); + +/* like isalnum(*s) in the C locale */ +LDAP_API(int) LDAP_CALL ldap_utf8isalnum( char* s ); +/* like isalpha(*s) in the C locale */ +LDAP_API(int) LDAP_CALL ldap_utf8isalpha( char* s ); +/* like isdigit(*s) in the C locale */ +LDAP_API(int) LDAP_CALL ldap_utf8isdigit( char* s ); +/* like isxdigit(*s) in the C locale */ +LDAP_API(int) LDAP_CALL ldap_utf8isxdigit(char* s ); +/* like isspace(*s) in the C locale */ +LDAP_API(int) LDAP_CALL ldap_utf8isspace( char* s ); + +#define LDAP_UTF8LEN(s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8len (s) : 1) +#define LDAP_UTF8NEXT(s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8next(s) : ( s)+1) +#define LDAP_UTF8INC(s) ((0x80 & *(unsigned char*)(s)) ? s=ldap_utf8next(s) : ++s) + +#define LDAP_UTF8PREV(s) ldap_utf8prev(s) +#define LDAP_UTF8DEC(s) (s=ldap_utf8prev(s)) + +#define LDAP_UTF8COPY(d,s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8copy(d,s) : ((*(d) = *(s)), 1)) +#define LDAP_UTF8GETCC(s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8getcc (&s) : *s++) +#define LDAP_UTF8GETC(s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8getcc ((const char**)&s) : *s++) + +/* SASL options */ +#define LDAP_OPT_X_SASL_MECH 0x6100 +#define LDAP_OPT_X_SASL_REALM 0x6101 +#define LDAP_OPT_X_SASL_AUTHCID 0x6102 +#define LDAP_OPT_X_SASL_AUTHZID 0x6103 +#define LDAP_OPT_X_SASL_SSF 0x6104 /* read-only */ +#define LDAP_OPT_X_SASL_SSF_EXTERNAL 0x6105 /* write-only */ +#define LDAP_OPT_X_SASL_SECPROPS 0x6106 /* write-only */ +#define LDAP_OPT_X_SASL_SSF_MIN 0x6107 +#define LDAP_OPT_X_SASL_SSF_MAX 0x6108 +#define LDAP_OPT_X_SASL_MAXBUFSIZE 0x6109 + +/* ldap_interactive_bind_s Interaction flags + * Interactive: prompt always - REQUIRED + */ +#define LDAP_SASL_AUTOMATIC 0U /* only prompt for missing items not supplied as defaults */ +#define LDAP_SASL_INTERACTIVE 1U /* prompt for everything and print defaults, if any */ +#define LDAP_SASL_QUIET 2U /* no prompts - only use defaults (e.g. for non-interactive apps) */ + +/* + * V3 SASL Interaction Function Callback Prototype + * when using Cyrus SASL, interact is pointer to sasl_interact_t + * should likely passed in a control (and provided controls) + */ +typedef int (LDAP_SASL_INTERACT_PROC) + (LDAP *ld, unsigned flags, void* defaults, void *interact ); + +LDAP_API(int) LDAP_CALL ldap_sasl_interactive_bind_s ( + LDAP *ld, + const char *dn, /* usually NULL */ + const char *saslMechanism, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults ); + +LDAP_API(int) LDAP_CALL ldap_sasl_interactive_bind_ext_s ( + LDAP *ld, + const char *dn, /* usually NULL */ + const char *saslMechanism, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults, + LDAPControl ***responseControls ); + +/* + * Password modify functions + */ +LDAP_API(int) LDAP_CALL ldap_passwd( LDAP *ld, struct berval *userid, + struct berval *oldpasswd, struct berval *newpasswd, + LDAPControl **serverctrls, LDAPControl **clientctrls, + int *msgidp ); + +LDAP_API(int) LDAP_CALL ldap_passwd_s( LDAP *ld, struct berval *userid, + struct berval *oldpasswd, struct berval *newpasswd, + struct berval *genpasswd, LDAPControl **serverctrls, + LDAPControl **clientctrls ); + +LDAP_API(int) LDAP_CALL ldap_parse_passwd( LDAP *ld, LDAPMessage *result, + struct berval *genpasswd ); + +/* + * in reslist.c + */ +LDAP_API(LDAPMessage *) LDAP_CALL ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e ); +LDAP_API(void) LDAP_CALL ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e ); + +#ifdef __cplusplus +} +#endif +#endif /* _LDAP_EXTENSION_H */ + diff --git a/ldap/c-sdk/include/ldap-platform.h b/ldap/c-sdk/include/ldap-platform.h new file mode 100644 index 0000000000..fee8fcf664 --- /dev/null +++ b/ldap/c-sdk/include/ldap-platform.h @@ -0,0 +1,91 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap-platform.h - platform transparency */ + +#ifndef _LDAP_PLATFORM_H +#define _LDAP_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (WIN32) || defined (_WIN32) || defined( _CONSOLE ) +#include +# if defined( _WINDOWS ) +# include +# endif +#elif defined(macintosh) +#ifndef LDAP_TYPE_TIMEVAL_DEFINED +#include +#endif +#ifndef LDAP_TYPE_SOCKET_DEFINED /* API extension */ +#include "macsocket.h" +#endif +#else /* everything else, e.g., Unix */ +#ifndef LDAP_TYPE_TIMEVAL_DEFINED +#include +#endif +#ifndef LDAP_TYPE_SOCKET_DEFINED /* API extension */ +#include +#include +#endif +#endif + +#ifdef _AIX +#include +#endif /* _AIX */ + +#ifdef XP_OS2 +#include +#endif /* XP_OS2 */ + +/* + * LDAP_API macro definition: + */ +#ifndef LDAP_API +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_API(rt) rt +#else /* _WINDOWS */ +#define LDAP_API(rt) rt +#endif /* _WINDOWS */ +#endif /* LDAP_API */ + +#ifdef __cplusplus +} +#endif +#endif /* _LDAP_PLATFORM_H */ diff --git a/ldap/c-sdk/include/ldap-standard.h b/ldap/c-sdk/include/ldap-standard.h new file mode 100644 index 0000000000..7e632cb1e7 --- /dev/null +++ b/ldap/c-sdk/include/ldap-standard.h @@ -0,0 +1,457 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap-standard.h - standards base header file for libldap */ +/* This file contain the defines and function prototypes matching */ +/* very closely to the latest LDAP C API draft */ + +#ifndef _LDAP_STANDARD_H +#define _LDAP_STANDARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ldap-platform.h" + +#include "lber.h" + +#define LDAP_PORT 389 +#define LDAPS_PORT 636 +#define LDAP_VERSION2 2 +#define LDAP_VERSION3 3 +#define LDAP_VERSION_MIN LDAP_VERSION1 +#define LDAP_VERSION_MAX LDAP_VERSION3 + +#define LDAP_VENDOR_VERSION 607 /* version # * 100 */ +#define LDAP_VENDOR_NAME "mozilla.org" +/* + * The following will be an RFC number once the LDAP C API Internet Draft + * is published as a Proposed Standard RFC. For now we use 2000 + the + * draft revision number (currently 5) since we are close to compliance + * with revision 5 of the draft. + */ +#define LDAP_API_VERSION 2005 + +/* special values that may appear in the attributes field of a SearchRequest. + */ +#define LDAP_NO_ATTRS "1.1" +#define LDAP_ALL_USER_ATTRS "*" + +/* + * Standard options (used with ldap_set_option() and ldap_get_option): + */ +#define LDAP_OPT_API_INFO 0x00 /* 0 */ +#define LDAP_OPT_DEREF 0x02 /* 2 */ +#define LDAP_OPT_SIZELIMIT 0x03 /* 3 */ +#define LDAP_OPT_TIMELIMIT 0x04 /* 4 */ +#define LDAP_OPT_REFERRALS 0x08 /* 8 */ +#define LDAP_OPT_RESTART 0x09 /* 9 */ +#define LDAP_OPT_PROTOCOL_VERSION 0x11 /* 17 */ +#define LDAP_OPT_SERVER_CONTROLS 0x12 /* 18 */ +#define LDAP_OPT_CLIENT_CONTROLS 0x13 /* 19 */ +#define LDAP_OPT_API_FEATURE_INFO 0x15 /* 21 */ +#define LDAP_OPT_HOST_NAME 0x30 /* 48 */ +#define LDAP_OPT_ERROR_NUMBER 0x31 /* 49 */ +#define LDAP_OPT_ERROR_STRING 0x32 /* 50 */ +#define LDAP_OPT_MATCHED_DN 0x33 /* 51 */ + +/* + * Well-behaved private and experimental extensions will use option values + * between 0x4000 (16384) and 0x7FFF (32767) inclusive. + */ +#define LDAP_OPT_PRIVATE_EXTENSION_BASE 0x4000 /* to 0x7FFF inclusive */ + +/* for on/off options */ +#define LDAP_OPT_ON ((void *)1) +#define LDAP_OPT_OFF ((void *)0) + +typedef struct ldap LDAP; /* opaque connection handle */ +typedef struct ldapmsg LDAPMessage; /* opaque result/entry handle */ + +/* structure representing an LDAP modification */ +typedef struct ldapmod { + int mod_op; /* kind of mod + form of values*/ +#define LDAP_MOD_ADD 0x00 +#define LDAP_MOD_DELETE 0x01 +#define LDAP_MOD_REPLACE 0x02 +#define LDAP_MOD_BVALUES 0x80 + char *mod_type; /* attribute name to modify */ + union mod_vals_u { + char **modv_strvals; + struct berval **modv_bvals; + } mod_vals; /* values to add/delete/replace */ +#define mod_values mod_vals.modv_strvals +#define mod_bvalues mod_vals.modv_bvals +} LDAPMod; + + +/* + * structure for holding ldapv3 controls + */ +typedef struct ldapcontrol { + char *ldctl_oid; + struct berval ldctl_value; + char ldctl_iscritical; +} LDAPControl; + + +/* + * LDAP API information. Can be retrieved by using a sequence like: + * + * LDAPAPIInfo ldai; + * ldai.ldapai_info_version = LDAP_API_INFO_VERSION; + * if ( ldap_get_option( NULL, LDAP_OPT_API_INFO, &ldia ) == 0 ) ... + */ +#define LDAP_API_INFO_VERSION 1 +typedef struct ldapapiinfo { + int ldapai_info_version; /* version of this struct (1) */ + int ldapai_api_version; /* revision of API supported */ + int ldapai_protocol_version; /* highest LDAP version supported */ + char **ldapai_extensions; /* names of API extensions */ + char *ldapai_vendor_name; /* name of supplier */ + int ldapai_vendor_version; /* supplier-specific version times 100 */ +} LDAPAPIInfo; + + +/* + * LDAP API extended features info. Can be retrieved by using a sequence like: + * + * LDAPAPIFeatureInfo ldfi; + * ldfi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; + * ldfi.ldapaif_name = "VIRTUAL_LIST_VIEW"; + * if ( ldap_get_option( NULL, LDAP_OPT_API_FEATURE_INFO, &ldfi ) == 0 ) ... + */ +#define LDAP_FEATURE_INFO_VERSION 1 +typedef struct ldap_apifeature_info { + int ldapaif_info_version; /* version of this struct (1) */ + char *ldapaif_name; /* name of supported feature */ + int ldapaif_version; /* revision of supported feature */ +} LDAPAPIFeatureInfo; + + +/* possible result types a server can return */ +#define LDAP_RES_BIND 0x61L /* 97 */ +#define LDAP_RES_SEARCH_ENTRY 0x64L /* 100 */ +#define LDAP_RES_SEARCH_RESULT 0x65L /* 101 */ +#define LDAP_RES_MODIFY 0x67L /* 103 */ +#define LDAP_RES_ADD 0x69L /* 105 */ +#define LDAP_RES_DELETE 0x6BL /* 107 */ +#define LDAP_RES_MODDN 0x6DL /* 109 */ +#define LDAP_RES_COMPARE 0x6FL /* 111 */ +#define LDAP_RES_SEARCH_REFERENCE 0x73L /* 115 */ +#define LDAP_RES_EXTENDED 0x78L /* 120 */ + +/* Special values for ldap_result() "msgid" parameter */ +#define LDAP_RES_ANY (-1) +#define LDAP_RES_UNSOLICITED 0 + +/* built-in SASL methods */ +#define LDAP_SASL_SIMPLE 0 /* special value used for simple bind */ + +/* search scopes */ +#define LDAP_SCOPE_BASE 0x00 +#define LDAP_SCOPE_ONELEVEL 0x01 +#define LDAP_SCOPE_SUBTREE 0x02 + +/* alias dereferencing */ +#define LDAP_DEREF_NEVER 0x00 +#define LDAP_DEREF_SEARCHING 0x01 +#define LDAP_DEREF_FINDING 0x02 +#define LDAP_DEREF_ALWAYS 0x03 + +/* predefined size/time limits */ +#define LDAP_NO_LIMIT 0 + +/* allowed values for "all" ldap_result() parameter */ +#define LDAP_MSG_ONE 0x00 +#define LDAP_MSG_ALL 0x01 +#define LDAP_MSG_RECEIVED 0x02 + +/* possible error codes we can be returned */ +#define LDAP_SUCCESS 0x00 /* 0 */ +#define LDAP_OPERATIONS_ERROR 0x01 /* 1 */ +#define LDAP_PROTOCOL_ERROR 0x02 /* 2 */ +#define LDAP_TIMELIMIT_EXCEEDED 0x03 /* 3 */ +#define LDAP_SIZELIMIT_EXCEEDED 0x04 /* 4 */ +#define LDAP_COMPARE_FALSE 0x05 /* 5 */ +#define LDAP_COMPARE_TRUE 0x06 /* 6 */ +#define LDAP_STRONG_AUTH_NOT_SUPPORTED 0x07 /* 7 */ +#define LDAP_STRONG_AUTH_REQUIRED 0x08 /* 8 */ +#define LDAP_REFERRAL 0x0a /* 10 - LDAPv3 */ +#define LDAP_ADMINLIMIT_EXCEEDED 0x0b /* 11 - LDAPv3 */ +#define LDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* 12 - LDAPv3 */ +#define LDAP_CONFIDENTIALITY_REQUIRED 0x0d /* 13 */ +#define LDAP_SASL_BIND_IN_PROGRESS 0x0e /* 14 - LDAPv3 */ + +#define LDAP_NO_SUCH_ATTRIBUTE 0x10 /* 16 */ +#define LDAP_UNDEFINED_TYPE 0x11 /* 17 */ +#define LDAP_INAPPROPRIATE_MATCHING 0x12 /* 18 */ +#define LDAP_CONSTRAINT_VIOLATION 0x13 /* 19 */ +#define LDAP_TYPE_OR_VALUE_EXISTS 0x14 /* 20 */ +#define LDAP_INVALID_SYNTAX 0x15 /* 21 */ + +#define LDAP_NO_SUCH_OBJECT 0x20 /* 32 */ +#define LDAP_ALIAS_PROBLEM 0x21 /* 33 */ +#define LDAP_INVALID_DN_SYNTAX 0x22 /* 34 */ +#define LDAP_IS_LEAF 0x23 /* 35 (not used in LDAPv3) */ +#define LDAP_ALIAS_DEREF_PROBLEM 0x24 /* 36 */ + +#define LDAP_INAPPROPRIATE_AUTH 0x30 /* 48 */ +#define LDAP_INVALID_CREDENTIALS 0x31 /* 49 */ +#define LDAP_INSUFFICIENT_ACCESS 0x32 /* 50 */ +#define LDAP_BUSY 0x33 /* 51 */ +#define LDAP_UNAVAILABLE 0x34 /* 52 */ +#define LDAP_UNWILLING_TO_PERFORM 0x35 /* 53 */ +#define LDAP_LOOP_DETECT 0x36 /* 54 */ + +#define LDAP_NAMING_VIOLATION 0x40 /* 64 */ +#define LDAP_OBJECT_CLASS_VIOLATION 0x41 /* 65 */ +#define LDAP_NOT_ALLOWED_ON_NONLEAF 0x42 /* 66 */ +#define LDAP_NOT_ALLOWED_ON_RDN 0x43 /* 67 */ +#define LDAP_ALREADY_EXISTS 0x44 /* 68 */ +#define LDAP_NO_OBJECT_CLASS_MODS 0x45 /* 69 */ +#define LDAP_RESULTS_TOO_LARGE 0x46 /* 70 - CLDAP */ +#define LDAP_AFFECTS_MULTIPLE_DSAS 0x47 /* 71 */ + +#define LDAP_OTHER 0x50 /* 80 */ +#define LDAP_SERVER_DOWN 0x51 /* 81 */ +#define LDAP_LOCAL_ERROR 0x52 /* 82 */ +#define LDAP_ENCODING_ERROR 0x53 /* 83 */ +#define LDAP_DECODING_ERROR 0x54 /* 84 */ +#define LDAP_TIMEOUT 0x55 /* 85 */ +#define LDAP_AUTH_UNKNOWN 0x56 /* 86 */ +#define LDAP_FILTER_ERROR 0x57 /* 87 */ +#define LDAP_USER_CANCELLED 0x58 /* 88 */ +#define LDAP_PARAM_ERROR 0x59 /* 89 */ +#define LDAP_NO_MEMORY 0x5a /* 90 */ +#define LDAP_CONNECT_ERROR 0x5b /* 91 */ +#define LDAP_NOT_SUPPORTED 0x5c /* 92 - LDAPv3 */ +#define LDAP_CONTROL_NOT_FOUND 0x5d /* 93 - LDAPv3 */ +#define LDAP_NO_RESULTS_RETURNED 0x5e /* 94 - LDAPv3 */ +#define LDAP_MORE_RESULTS_TO_RETURN 0x5f /* 95 - LDAPv3 */ +#define LDAP_CLIENT_LOOP 0x60 /* 96 - LDAPv3 */ +#define LDAP_REFERRAL_LIMIT_EXCEEDED 0x61 /* 97 - LDAPv3 */ + +/* + * LDAPv3 unsolicited notification messages we know about + */ +#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" + +/* + * Client controls we know about + */ +#define LDAP_CONTROL_REFERRALS "1.2.840.113556.1.4.616" + +/* + * Initializing an ldap sesssion, set session handle options, and + * closing an ldap session functions + * + * NOTE: If you want to use IPv6, you must use prldap creating a LDAP handle + * with prldap_init instead of ldap_init. Or install the NSPR functions + * by calling prldap_install_routines. (See the nspr samples in examples) + */ +LDAP_API(LDAP *) LDAP_CALL ldap_init( const char *defhost, int defport ); +LDAP_API(int) LDAP_CALL ldap_set_option( LDAP *ld, int option, + const void *optdata ); +LDAP_API(int) LDAP_CALL ldap_get_option( LDAP *ld, int option, void *optdata ); +LDAP_API(int) LDAP_CALL ldap_unbind( LDAP *ld ); +LDAP_API(int) LDAP_CALL ldap_unbind_s( LDAP *ld ); + +/* + * perform ldap operations + */ +LDAP_API(int) LDAP_CALL ldap_abandon( LDAP *ld, int msgid ); +LDAP_API(int) LDAP_CALL ldap_add( LDAP *ld, const char *dn, LDAPMod **attrs ); +LDAP_API(int) LDAP_CALL ldap_add_s( LDAP *ld, const char *dn, LDAPMod **attrs ); +LDAP_API(int) LDAP_CALL ldap_simple_bind( LDAP *ld, const char *who, + const char *passwd ); +LDAP_API(int) LDAP_CALL ldap_simple_bind_s( LDAP *ld, const char *who, + const char *passwd ); +LDAP_API(int) LDAP_CALL ldap_modify( LDAP *ld, const char *dn, LDAPMod **mods ); +LDAP_API(int) LDAP_CALL ldap_modify_s( LDAP *ld, const char *dn, + LDAPMod **mods ); +LDAP_API(int) LDAP_CALL ldap_compare( LDAP *ld, const char *dn, + const char *attr, const char *value ); +LDAP_API(int) LDAP_CALL ldap_compare_s( LDAP *ld, const char *dn, + const char *attr, const char *value ); +LDAP_API(int) LDAP_CALL ldap_delete( LDAP *ld, const char *dn ); +LDAP_API(int) LDAP_CALL ldap_delete_s( LDAP *ld, const char *dn ); +LDAP_API(int) LDAP_CALL ldap_search( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly ); +LDAP_API(int) LDAP_CALL ldap_search_s( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, LDAPMessage **res ); +LDAP_API(int) LDAP_CALL ldap_search_st( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, + struct timeval *timeout, LDAPMessage **res ); + +/* + * obtain result from ldap operation + */ +LDAP_API(int) LDAP_CALL ldap_result( LDAP *ld, int msgid, int all, + struct timeval *timeout, LDAPMessage **result ); + +/* + * peeking inside LDAP Messages and deallocating LDAP Messages + */ +LDAP_API(int) LDAP_CALL ldap_msgfree( LDAPMessage *lm ); +LDAP_API(int) LDAP_CALL ldap_msgid( LDAPMessage *lm ); +LDAP_API(int) LDAP_CALL ldap_msgtype( LDAPMessage *lm ); + + +/* + * Routines to parse/deal with results and errors returned + */ +LDAP_API(char *) LDAP_CALL ldap_err2string( int err ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_first_entry( LDAP *ld, + LDAPMessage *chain ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_next_entry( LDAP *ld, + LDAPMessage *entry ); +LDAP_API(int) LDAP_CALL ldap_count_entries( LDAP *ld, LDAPMessage *chain ); +LDAP_API(char *) LDAP_CALL ldap_get_dn( LDAP *ld, LDAPMessage *entry ); +LDAP_API(char *) LDAP_CALL ldap_dn2ufn( const char *dn ); +LDAP_API(char **) LDAP_CALL ldap_explode_dn( const char *dn, + const int notypes ); +LDAP_API(char **) LDAP_CALL ldap_explode_rdn( const char *rdn, + const int notypes ); +LDAP_API(char *) LDAP_CALL ldap_first_attribute( LDAP *ld, LDAPMessage *entry, + BerElement **ber ); +LDAP_API(char *) LDAP_CALL ldap_next_attribute( LDAP *ld, LDAPMessage *entry, + BerElement *ber ); +LDAP_API(char **) LDAP_CALL ldap_get_values( LDAP *ld, LDAPMessage *entry, + const char *target ); +LDAP_API(struct berval **) LDAP_CALL ldap_get_values_len( LDAP *ld, + LDAPMessage *entry, const char *target ); +LDAP_API(int) LDAP_CALL ldap_count_values( char **vals ); +LDAP_API(int) LDAP_CALL ldap_count_values_len( struct berval **vals ); +LDAP_API(void) LDAP_CALL ldap_value_free( char **vals ); +LDAP_API(void) LDAP_CALL ldap_value_free_len( struct berval **vals ); +LDAP_API(void) LDAP_CALL ldap_memfree( void *p ); + + +/* + * LDAPv3 extended operation calls + */ +/* + * Note: all of the new asynchronous calls return an LDAP error code, + * not a message id. A message id is returned via the int *msgidp + * parameter (usually the last parameter) if appropriate. + */ +LDAP_API(int) LDAP_CALL ldap_abandon_ext( LDAP *ld, int msgid, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_add_ext( LDAP *ld, const char *dn, LDAPMod **attrs, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_add_ext_s( LDAP *ld, const char *dn, + LDAPMod **attrs, LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_sasl_bind( LDAP *ld, const char *dn, + const char *mechanism, const struct berval *cred, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_sasl_bind_s( LDAP *ld, const char *dn, + const char *mechanism, const struct berval *cred, + LDAPControl **serverctrls, LDAPControl **clientctrls, + struct berval **servercredp ); +LDAP_API(int) LDAP_CALL ldap_modify_ext( LDAP *ld, const char *dn, + LDAPMod **mods, LDAPControl **serverctrls, LDAPControl **clientctrls, + int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_modify_ext_s( LDAP *ld, const char *dn, + LDAPMod **mods, LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_rename( LDAP *ld, const char *dn, + const char *newrdn, const char *newparent, int deleteoldrdn, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_rename_s( LDAP *ld, const char *dn, + const char *newrdn, const char *newparent, int deleteoldrdn, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_compare_ext( LDAP *ld, const char *dn, + const char *attr, const struct berval *bvalue, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_compare_ext_s( LDAP *ld, const char *dn, + const char *attr, const struct berval *bvalue, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_delete_ext( LDAP *ld, const char *dn, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_delete_ext_s( LDAP *ld, const char *dn, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +LDAP_API(int) LDAP_CALL ldap_search_ext( LDAP *ld, const char *base, + int scope, const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + struct timeval *timeoutp, int sizelimit, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_search_ext_s( LDAP *ld, const char *base, + int scope, const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + struct timeval *timeoutp, int sizelimit, LDAPMessage **res ); +LDAP_API(int) LDAP_CALL ldap_extended_operation( LDAP *ld, + const char *requestoid, const struct berval *requestdata, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ); +LDAP_API(int) LDAP_CALL ldap_extended_operation_s( LDAP *ld, + const char *requestoid, const struct berval *requestdata, + LDAPControl **serverctrls, LDAPControl **clientctrls, + char **retoidp, struct berval **retdatap ); +LDAP_API(int) LDAP_CALL ldap_unbind_ext( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls ); + + +/* + * LDAPv3 extended parsing / result handling calls + */ +LDAP_API(int) LDAP_CALL ldap_parse_sasl_bind_result( LDAP *ld, + LDAPMessage *res, struct berval **servercredp, int freeit ); +LDAP_API(int) LDAP_CALL ldap_parse_result( LDAP *ld, LDAPMessage *res, + int *errcodep, char **matcheddnp, char **errmsgp, char ***referralsp, + LDAPControl ***serverctrlsp, int freeit ); +LDAP_API(int) LDAP_CALL ldap_parse_extended_result( LDAP *ld, LDAPMessage *res, + char **retoidp, struct berval **retdatap, int freeit ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_first_message( LDAP *ld, + LDAPMessage *res ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_next_message( LDAP *ld, + LDAPMessage *msg ); +LDAP_API(int) LDAP_CALL ldap_count_messages( LDAP *ld, LDAPMessage *res ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_first_reference( LDAP *ld, + LDAPMessage *res ); +LDAP_API(LDAPMessage *) LDAP_CALL ldap_next_reference( LDAP *ld, + LDAPMessage *ref ); +LDAP_API(int) LDAP_CALL ldap_count_references( LDAP *ld, LDAPMessage *res ); +LDAP_API(int) LDAP_CALL ldap_parse_reference( LDAP *ld, LDAPMessage *ref, + char ***referralsp, LDAPControl ***serverctrlsp, int freeit ); +LDAP_API(int) LDAP_CALL ldap_get_entry_controls( LDAP *ld, LDAPMessage *entry, + LDAPControl ***serverctrlsp ); +LDAP_API(void) LDAP_CALL ldap_control_free( LDAPControl *ctrl ); +LDAP_API(void) LDAP_CALL ldap_controls_free( LDAPControl **ctrls ); + +#ifdef __cplusplus +} +#endif +#endif /* _LDAP_STANDARD_H */ diff --git a/ldap/c-sdk/include/ldap-to-be-deprecated.h b/ldap/c-sdk/include/ldap-to-be-deprecated.h new file mode 100644 index 0000000000..73404da0f6 --- /dev/null +++ b/ldap/c-sdk/include/ldap-to-be-deprecated.h @@ -0,0 +1,193 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap-to-be-deprecated.h - functions and declaration which will be + * deprecated in a future release. + * + * A deprecated API is an API that we recommend you no longer use, + * due to improvements in the LDAP C SDK. While deprecated APIs are + * currently still implemented, they may be removed in future + * implementations, and we recommend using other APIs. + * + * This header file will act as a first warning before moving functions + * into an unsupported/deprecated state. If your favorite application + * depend on any declaration and defines, and there is a good reason + * for not porting to new functions, Speak up now or they may disappear + * in a future release + */ + +#ifndef _LDAP_TOBE_DEPRECATED_H +#define _LDAP_TOBE_DEPRECATED_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * I/O function callbacks option (an API extension -- + * LDAP_API_FEATURE_X_IO_FUNCTIONS). + * Use of the extended I/O functions instead is recommended + */ +#define LDAP_OPT_IO_FN_PTRS 0x0B /* 11 - API extension */ + +/* + * I/O callback functions (note that types for the read and write callbacks + * are actually in lber.h): + */ +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_SELECT_CALLBACK)( int nfds, + fd_set *readfds, fd_set *writefds, fd_set *errorfds, + struct timeval *timeout ); +typedef LBER_SOCKET (LDAP_C LDAP_CALLBACK LDAP_IOF_SOCKET_CALLBACK)( + int domain, int type, int protocol ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_IOCTL_CALLBACK)( LBER_SOCKET s, + int option, ... ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_CONNECT_CALLBACK )( + LBER_SOCKET s, struct sockaddr *name, int namelen ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_CLOSE_CALLBACK )( + LBER_SOCKET s ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_IOF_SSL_ENABLE_CALLBACK )( + LBER_SOCKET s ); + +/* + * Structure to hold I/O function pointers: + */ +struct ldap_io_fns { + LDAP_IOF_READ_CALLBACK *liof_read; + LDAP_IOF_WRITE_CALLBACK *liof_write; + LDAP_IOF_SELECT_CALLBACK *liof_select; + LDAP_IOF_SOCKET_CALLBACK *liof_socket; + LDAP_IOF_IOCTL_CALLBACK *liof_ioctl; + LDAP_IOF_CONNECT_CALLBACK *liof_connect; + LDAP_IOF_CLOSE_CALLBACK *liof_close; + LDAP_IOF_SSL_ENABLE_CALLBACK *liof_ssl_enable; +}; + +/* + * DNS resolver callbacks (an API extension --LDAP_API_FEATURE_X_DNS_FUNCTIONS). + * Note that gethostbyaddr() is not currently used. + */ +#define LDAP_OPT_DNS_FN_PTRS 0x60 /* 96 - API extension */ + +typedef struct LDAPHostEnt { + char *ldaphe_name; /* official name of host */ + char **ldaphe_aliases; /* alias list */ + int ldaphe_addrtype; /* host address type */ + int ldaphe_length; /* length of address */ + char **ldaphe_addr_list; /* list of addresses from name server */ +} LDAPHostEnt; + +typedef LDAPHostEnt * (LDAP_C LDAP_CALLBACK LDAP_DNSFN_GETHOSTBYNAME)( + const char *name, LDAPHostEnt *result, char *buffer, + int buflen, int *statusp, void *extradata ); +typedef LDAPHostEnt * (LDAP_C LDAP_CALLBACK LDAP_DNSFN_GETHOSTBYADDR)( + const char *addr, int length, int type, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ); +typedef int (LDAP_C LDAP_CALLBACK LDAP_DNSFN_GETPEERNAME)( + LDAP *ld, struct sockaddr *netaddr, char *buffer, int buflen); + +struct ldap_dns_fns { + void *lddnsfn_extradata; + int lddnsfn_bufsize; + LDAP_DNSFN_GETHOSTBYNAME *lddnsfn_gethostbyname; + LDAP_DNSFN_GETHOSTBYADDR *lddnsfn_gethostbyaddr; + LDAP_DNSFN_GETPEERNAME *lddnsfn_getpeername; +}; + +/* + * experimental DN format support + */ +LDAP_API(char **) LDAP_CALL ldap_explode_dns( const char *dn ); +LDAP_API(int) LDAP_CALL ldap_is_dns_dn( const char *dn ); + + +/* + * user friendly naming/searching routines + */ +typedef int (LDAP_C LDAP_CALLBACK LDAP_CANCELPROC_CALLBACK)( void *cl ); +LDAP_API(int) LDAP_CALL ldap_ufn_search_c( LDAP *ld, char *ufn, + char **attrs, int attrsonly, LDAPMessage **res, + LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm ); +LDAP_API(int) LDAP_CALL ldap_ufn_search_ct( LDAP *ld, char *ufn, + char **attrs, int attrsonly, LDAPMessage **res, + LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, + char *tag1, char *tag2, char *tag3 ); +LDAP_API(int) LDAP_CALL ldap_ufn_search_s( LDAP *ld, char *ufn, + char **attrs, int attrsonly, LDAPMessage **res ); +LDAP_API(LDAPFiltDesc *) LDAP_CALL ldap_ufn_setfilter( LDAP *ld, char *fname ); +LDAP_API(void) LDAP_CALL ldap_ufn_setprefix( LDAP *ld, char *prefix ); +LDAP_API(int) LDAP_C ldap_ufn_timeout( void *tvparam ); + +/* + * utility routines + */ +LDAP_API(int) LDAP_CALL ldap_charray_add( char ***a, char *s ); +LDAP_API(int) LDAP_CALL ldap_charray_merge( char ***a, char **s ); +LDAP_API(void) LDAP_CALL ldap_charray_free( char **array ); +LDAP_API(int) LDAP_CALL ldap_charray_inlist( char **a, char *s ); +LDAP_API(char **) LDAP_CALL ldap_charray_dup( char **a ); +LDAP_API(char **) LDAP_CALL ldap_str2charray( char *str, char *brkstr ); +LDAP_API(int) LDAP_CALL ldap_charray_position( char **a, char *s ); + +/* from ldap_ssl.h - the pkcs function and declaration */ +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_TOKEN_CALLBACK)(void *context, char **tokenname); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_PIN_CALLBACK)(void *context, const char *tokenname, char **tokenpin); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_CERTPATH_CALLBACK)(void *context, char **certpath); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_KEYPATH_CALLBACK)(void *context,char **keypath); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_MODPATH_CALLBACK)(void *context, char **modulepath); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_CERTNAME_CALLBACK)(void *context, char **certname); +typedef int (LDAP_C LDAP_CALLBACK LDAP_PKCS_GET_DONGLEFILENAME_CALLBACK)(void *context, char **filename); + +#define PKCS_STRUCTURE_ID 1 +struct ldapssl_pkcs_fns { + int local_structure_id; + void *local_data; + LDAP_PKCS_GET_CERTPATH_CALLBACK *pkcs_getcertpath; + LDAP_PKCS_GET_CERTNAME_CALLBACK *pkcs_getcertname; + LDAP_PKCS_GET_KEYPATH_CALLBACK *pkcs_getkeypath; + LDAP_PKCS_GET_MODPATH_CALLBACK *pkcs_getmodpath; + LDAP_PKCS_GET_PIN_CALLBACK *pkcs_getpin; + LDAP_PKCS_GET_TOKEN_CALLBACK *pkcs_gettokenname; + LDAP_PKCS_GET_DONGLEFILENAME_CALLBACK *pkcs_getdonglefilename; + +}; + +LDAP_API(int) LDAP_CALL ldapssl_pkcs_init( const struct ldapssl_pkcs_fns *pfns); + +#ifdef __cplusplus +} +#endif +#endif /* _LDAP_TOBE_DEPRECATED_H */ diff --git a/ldap/c-sdk/include/ldap.h b/ldap/c-sdk/include/ldap.h new file mode 100644 index 0000000000..2eea7ada6e --- /dev/null +++ b/ldap/c-sdk/include/ldap.h @@ -0,0 +1,62 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* ldap.h - general header file for libldap */ + +#ifndef _LDAP_H +#define _LDAP_H + +/* Standard LDAP API functions and declarations */ +#include "ldap-standard.h" + +/* Extensions to the LDAP standard */ +#include "ldap-extension.h" + +/* A deprecated API is an API that we recommend you no longer use, + * due to improvements in the LDAP C SDK. While deprecated APIs are + * currently still implemented, they may be removed in future + * implementations, and we recommend using other APIs. + */ + +/* Soon-to-be deprecated functions and declarations */ +#include "ldap-to-be-deprecated.h" + +/* Deprecated functions and declarations */ +#include "ldap-deprecated.h" + +#endif /* _LDAP_H */ + diff --git a/ldap/c-sdk/include/ldap_ssl.h b/ldap/c-sdk/include/ldap_ssl.h new file mode 100644 index 0000000000..5c786a9085 --- /dev/null +++ b/ldap/c-sdk/include/ldap_ssl.h @@ -0,0 +1,254 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if !defined(LDAP_SSL_H) +#define LDAP_SSL_H + +/* ldap_ssl.h - prototypes for LDAP over SSL functions */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * these three defines resolve the SSL strength + * setting auth weak, diables all cert checking + * the CNCHECK tests for the man in the middle hack + */ +#define LDAPSSL_AUTH_WEAK 0 +#define LDAPSSL_AUTH_CERT 1 +#define LDAPSSL_AUTH_CNCHECK 2 + +/* + * an ExtendedRequest [LDAPv3] specifying the OID for the + * Start TLS operation: RFC 2830 + */ +#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" + +/* + * Initialize LDAP library for SSL + */ +LDAP * LDAP_CALL ldapssl_init( const char *defhost, int defport, + int defsecure ); + +/* + * Shutdown LDAP library for SSL : + * Perform necessary cleanup and attempt to shutdown NSS. All existing + * ld session handles should be ldap_unbind(ld) prior to calling this. + */ +int LDAP_CALL ldapssl_shutdown(); + +/* Initialize LDAP library for TLS(SSL) and sends StartTLS extended + * operation to the Directory Server. + * Returns LDAP_SUCCESS if all goes well. + */ +int LDAP_CALL ldap_start_tls_s( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls ); +/* + * Install I/O routines to make SSL over LDAP possible. + * Use this after ldap_init() or just use ldapssl_init() instead. + * Returns 0 if all goes well. + */ +int LDAP_CALL ldapssl_install_routines( LDAP *ld ); + + +/* The next four functions initialize the security code for SSL + * The first one ldapssl_client_init() does initialization for SSL only + * The next one supports server authentication using clientauth_init() + * and allows the caller to specify the ssl strength to use in order to + * verify the servers's certificate. + * The next one supports ldapssl_clientauth_init() intializes security + * for SSL for client authentication. The third function initializes + * security for doing SSL with client authentication, and PKCS, that is, + * the third function initializes the security module database (secmod.db). + * The parameters are as follows: + * const char *certdbpath - path to the cert file. This can be a shortcut + * to the directory name, if so cert7.db will be postfixed to the string. + * void *certdbhandle - Normally this is NULL. This memory will need + * to be freed. + * int needkeydb - boolean. Must be !=0 if client Authentification + * is required + * char *keydbpath - path to the key database. This can be a shortcut + * to the directory name, if so key3.db will be postfixed to the string. + * void *keydbhandle - Normally this is NULL, This memory will need + * to be freed + * int needsecmoddb - boolean. Must be !=0 to assure that the correct + * security module is loaded into memory + * char *secmodpath - path to the secmod. This can be a shortcut to the + * directory name, if so secmod.db will be postfixed to the string. + * + * These three functions are mutually exclusive. You can only call + * one. This means that, for a given process, you must call the + * appropriate initialization function for the life of the process. + */ + + +/* + * Initialize the secure parts (Security and SSL) of the runtime for use + * by a client application. This is only called once. + * Returns 0 if all goes well. + */ + +int LDAP_CALL ldapssl_client_init( + const char *certdbpath, void *certdbhandle ); + +/* + * Initialize the secure parts (Security and SSL) of the runtime for use + * by a client application using server authentication. This is only + * called once. + * + * ldapssl_serverauth_init() is a server-authentication only version of + * ldapssl_clientauth_init(). This function allows the sslstrength + * to be passed in. The sslstrength can take one of the following + * values: + * + * LDAPSSL_AUTH_WEAK: indicate that you accept the server's + * certificate without checking the CA who + * issued the certificate + * LDAPSSL_AUTH_CERT: indicates that you accept the server's + * certificate only if you trust the CA who + * issued the certificate + * LDAPSSL_AUTH_CNCHECK: + * indicates that you accept the server's + * certificate only if you trust the CA who + * issued the certificate and if the value + * of the cn attribute is the DNS hostname + * of the server. If this option is selected, + * please ensure that the "defhost" parameter + * passed to ldapssl_init() consist of only + * one hostname and not a list of hosts. + * Furthermore, the port number must be passed + * via the "defport" parameter, and cannot + * be passed via a host:port option. + * + * Returns 0 if all goes well. + */ + +int LDAP_CALL ldapssl_serverauth_init( + const char *certdbpath, void *certdbhandle, const int sslstrength ); + +/* + * Initialize the secure parts (Security and SSL) of the runtime for use + * by a client application that may want to do SSL client authentication. + * Returns 0 if all goes well. + */ + +int LDAP_CALL ldapssl_clientauth_init( + const char *certdbpath, void *certdbhandle, + const int needkeydb, const char *keydbpath, void *keydbhandle ); + +/* + * Initialize the secure parts (Security and SSL) of the runtime for use + * by a client application that may want to do SSL client authentication. + * + * Please see the description of the sslstrength value in the + * ldapssl_serverauth_init() function above and note the potential + * problems which can be caused by passing in wrong host & portname + * values. The same warning applies to the ldapssl_advclientauth_init() + * function. + * + * Returns 0 if all goes well. + */ + +int LDAP_CALL ldapssl_advclientauth_init( + const char *certdbpath, void *certdbhandle, + const int needkeydb, const char *keydbpath, void *keydbhandle, + const int needsecmoddb, const char *secmoddbpath, + const int sslstrength ); + + + +/* + * get a meaningful error string back from the security library + * this function should be called, if ldap_err2string doesn't + * identify the error code. + */ +const char * LDAP_CALL ldapssl_err2string( const int prerrno ); + + +/* + * Enable SSL client authentication on the given ld. + * Returns 0 if all goes well. + */ +int LDAP_CALL ldapssl_enable_clientauth( LDAP *ld, char *keynickname, + char *keypasswd, char *certnickname ); + +/* + * Set the SSL strength for an existing SSL-enabled LDAP session handle. + * + * See the description of ldapssl_serverauth_init() above for valid + * sslstrength values. If ld is NULL, the default for new LDAP session + * handles is set. + * + * Returns 0 if all goes well. + */ +int LDAP_CALL ldapssl_set_strength( LDAP *ld, int sslstrength ); + + +/* + * Set or get SSL options for an existing SSL-enabled LDAP session handle. + * If ld is NULL, the default options used for all future LDAP SSL sessions + * are the ones affected. The option values are specific to the underlying + * SSL provider; see ssl.h within the Network Security Services (NSS) + * distribution for the options supported by NSS (the default SSL provider). + * + * The ldapssl_set_option() function should be called before any LDAP + * connections are created. + * + * Both functions return 0 if all goes well. + */ +int LDAP_CALL ldapssl_set_option( LDAP *ld, int option, int on ); +int LDAP_CALL ldapssl_get_option( LDAP *ld, int option, int *onp ); + +/* + * Import the file descriptor corresponding to the socket of an already + * open LDAP connection into SSL, and update the socket and session + * information accordingly. Returns 0 if all goes well. + */ +int LDAP_CALL ldapssl_import_fd ( LDAP *ld, int secure ); + +/* + * Reset an LDAP session from SSL to a non-secure status. Basically, + * this function undoes the work done by ldapssl_install_routines. + * Returns 0 if all goes well. + */ +int LDAP_CALL ldapssl_reset_to_nonsecure ( LDAP *ld ); + +#ifdef __cplusplus +} +#endif +#endif /* !defined(LDAP_SSL_H) */ diff --git a/ldap/c-sdk/include/ldaplog.h b/ldap/c-sdk/include/ldaplog.h new file mode 100644 index 0000000000..9f3f12e133 --- /dev/null +++ b/ldap/c-sdk/include/ldaplog.h @@ -0,0 +1,106 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _LDAPLOG_H +#define _LDAPLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LDAP_DEBUG_TRACE 0x00001 +#define LDAP_DEBUG_PACKETS 0x00002 +#define LDAP_DEBUG_ARGS 0x00004 +#define LDAP_DEBUG_CONNS 0x00008 +#define LDAP_DEBUG_BER 0x00010 +#define LDAP_DEBUG_FILTER 0x00020 +#define LDAP_DEBUG_CONFIG 0x00040 +#define LDAP_DEBUG_ACL 0x00080 +#define LDAP_DEBUG_STATS 0x00100 +#define LDAP_DEBUG_STATS2 0x00200 +#define LDAP_DEBUG_SHELL 0x00400 +#define LDAP_DEBUG_PARSE 0x00800 +#define LDAP_DEBUG_HOUSE 0x01000 +#define LDAP_DEBUG_REPL 0x02000 +#define LDAP_DEBUG_ANY 0x04000 +#define LDAP_DEBUG_CACHE 0x08000 +#define LDAP_DEBUG_PLUGIN 0x10000 + +/* debugging stuff */ +/* Disable by default */ +#define LDAPDebug( level, fmt, arg1, arg2, arg3 ) + +#ifdef LDAP_DEBUG +# undef LDAPDebug + +/* SLAPD_LOGGING should not be on for WINSOCK (16-bit Windows) */ +# if defined(SLAPD_LOGGING) +# ifdef _WIN32 + extern int *module_ldap_debug; +# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \ + { \ + if ( *module_ldap_debug & level ) { \ + slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \ + } \ + } +# else /* _WIN32 */ + extern int ldap_debug; +# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \ + { \ + if ( ldap_debug & level ) { \ + slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \ + } \ + } +# endif /* Win32 */ +# else /* no SLAPD_LOGGING */ + extern void ber_err_print( char * ); + extern int ldap_debug; +# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \ + if ( ldap_debug & level ) { \ + char msg[1024]; \ + snprintf( msg, sizeof(msg), fmt, arg1, arg2, arg3 ); \ + msg[sizeof(msg)-1] = '\0'; \ + ber_err_print( msg ); \ + } +# endif /* SLAPD_LOGGING */ +#endif /* LDAP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LDAP_H */ diff --git a/ldap/c-sdk/include/ldappr.h b/ldap/c-sdk/include/ldappr.h new file mode 100644 index 0000000000..04e710e1af --- /dev/null +++ b/ldap/c-sdk/include/ldappr.h @@ -0,0 +1,273 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef LDAP_PR_H +#define LDAP_PR_H + +#include "nspr.h" + +/* + * ldappr.h - prototypes for functions that tie libldap into NSPR (Netscape + * Portable Runtime). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Function: prldap_init(). + * + * Create a new LDAP session handle, but with NSPR I/O, threading, and DNS + * functions installed. + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * Returns an LDAP session handle (or NULL if an error occurs). + * + * NOTE: If you want to use IPv6, you must use prldap creating a LDAP handle + * with this function prldap_init. Prldap_init installs the appropriate + * set of NSPR functions and prevents calling deprecated functions accidentally. + */ +LDAP * LDAP_CALL prldap_init( const char *defhost, int defport, int shared ); + + +/* + * Function: prldap_install_routines(). + * + * Install NSPR I/O, threading, and DNS functions so they will be used by + * 'ld'. + * + * If 'ld' is NULL, the functions are installed as the default functions + * for all new LDAP * handles). + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_install_routines( LDAP *ld, int shared ); + + +/* + * Function: prldap_set_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * CONNECT, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, set + * an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the option is set as the default + * for all new prldap sessions. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_set_session_option( LDAP *ld, void *sessionarg, + int option, ... ); + + +/* + * Function: prldap_get_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * CONNECT, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, retrieve + * the setting for an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the default option value for all new + * new prldap sessions is retrieved. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_get_session_option( LDAP *ld, void *sessionarg, + int option, ... ); + + +/* + * Available options. + */ +/* + * PRLDAP_OPT_IO_MAX_TIMEOUT: the maximum time in milliseconds to + * block waiting for a network I/O operation to complete. + * + * Data type: int. + * + * These two special values from ldap-extension.h can also be used; + * + * LDAP_X_IO_TIMEOUT_NO_TIMEOUT + * LDAP_X_IO_TIMEOUT_NO_WAIT + */ +#define PRLDAP_OPT_IO_MAX_TIMEOUT 1 + + +/** + ** Note: the types and functions below are only useful for developers + ** who need to layer one or more custom extended I/O functions on top of + ** the standard NSPR I/O functions installed by a call to prldap_init() + ** or prldap_install_routines(). Layering can be accomplished after + ** prldap_init() or prldap_install_routines() has completed successfully + ** by: + ** + ** 1) Calling ldap_get_option( ..., LDAP_X_OPT_EXTIO_FN_PTRS, ... ). + ** + ** 2) Saving the function pointer of one or more of the standard functions. + ** + ** 3) Replacing one or more standard functions in the ldap_x_ext_io_fns + ** struct with new functions that optionally do some preliminary work, + ** call the standard function (via the function pointer saved in step 2), + ** and optionally do some followup work. + */ + +/* + * Data structure for session information. + * seinfo_size should be set to PRLDAP_SESSIONINFO_SIZE before use. + */ +struct prldap_session_private; + +typedef struct prldap_session_info { + int seinfo_size; + struct prldap_session_private *seinfo_appdata; +} PRLDAPSessionInfo; +#define PRLDAP_SESSIONINFO_SIZE sizeof( PRLDAPSessionInfo ) + + +/* + * Function: prldap_set_session_info(). + * + * Given an LDAP session handle or a session argument such is passed to + * CONNECT, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, + * set some application-specific data. If ld is NULL, arg is used. If + * both ld and arg are NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_set_session_info( LDAP *ld, void *sessionarg, + PRLDAPSessionInfo *seip ); + + +/* + * Function: prldap_get_session_info(). + * + * Given an LDAP session handle or a session argument such is passed to + * CONNECT, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, + * retrieve some application-specific data. If ld is NULL, arg is used. If + * both ld and arg are NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that seip points to are filled in). + */ +int LDAP_CALL prldap_get_session_info( LDAP *ld, void *sessionarg, + PRLDAPSessionInfo *seip ); + + +/* + * Data structure for socket specific information. + * Note: soinfo_size should be set to PRLDAP_SOCKETINFO_SIZE before use. + */ +struct prldap_socket_private; +typedef struct prldap_socket_info { + int soinfo_size; + PRFileDesc *soinfo_prfd; + struct prldap_socket_private *soinfo_appdata; +} PRLDAPSocketInfo; +#define PRLDAP_SOCKETINFO_SIZE sizeof( PRLDAPSocketInfo ) + + +/* + * Function: prldap_set_socket_info(). + * + * Given an integer fd and a socket argument such as those passed to the + * extended I/O callback functions, set socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + * + * Note: it is only safe to change soinfo_prfd from within the CONNECT + * extended I/O callback function. + */ +int LDAP_CALL prldap_set_socket_info( int fd, void *socketarg, + PRLDAPSocketInfo *soip ); + +/* + * Function: prldap_get_socket_info(). + * + * Given an integer fd and a socket argument such as those passed to the + * extended I/O callback functions, retrieve socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL prldap_get_socket_info( int fd, void *socketarg, + PRLDAPSocketInfo *soip ); + +/* + * Function: prldap_get_default_socket_info(). + * + * Given an LDAP session handle, retrieve socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL prldap_get_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ); + +/* + * Function: prldap_set_default_socket_info(). + * + * Given an LDAP session handle, set socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL prldap_set_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ); + +/* Function: prldap_is_installed() + * Check if NSPR routine is installed + */ +PRBool prldap_is_installed( LDAP *ld ); + +/* Function: prldap_import_connection(). + * Given a ldap handle with connection already done with ldap_init() + * installs NSPR routines and imports the original connection info. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_import_connection (LDAP *ld); + +#ifdef __cplusplus +} +#endif +#endif /* !defined(LDAP_PR_H) */ diff --git a/ldap/c-sdk/include/ldaprot.h b/ldap/c-sdk/include/ldaprot.h new file mode 100644 index 0000000000..98aec13c8d --- /dev/null +++ b/ldap/c-sdk/include/ldaprot.h @@ -0,0 +1,203 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _LDAPROT_H +#define _LDAPROT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LDAP_VERSION1 1 +#define LDAP_VERSION2 2 +#define LDAP_VERSION3 3 +#define LDAP_VERSION LDAP_VERSION3 + +#define COMPAT20 +#define COMPAT30 +#if defined(COMPAT20) || defined(COMPAT30) +#define COMPAT +#endif + +#define LDAP_URL_PREFIX "ldap://" +#define LDAP_URL_PREFIX_LEN 7 +#define LDAPS_URL_PREFIX "ldaps://" +#define LDAPS_URL_PREFIX_LEN 8 +#define LDAP_REF_STR "Referral:\n" +#define LDAP_REF_STR_LEN 10 + +/* + * specific LDAP instantiations of BER types we know about + */ + +/* general stuff */ +#define LDAP_TAG_MESSAGE 0x30L /* tag is 16 + constructed bit */ +#define OLD_LDAP_TAG_MESSAGE 0x10L /* forgot the constructed bit */ +#define LDAP_TAG_MSGID 0x02L /* INTEGER */ +#define LDAP_TAG_LDAPDN 0x04L /* OCTET STRING */ +#define LDAP_TAG_CONTROLS 0xa0L /* context specific + constructed + 0 */ +#define LDAP_TAG_REFERRAL 0xa3L /* context specific + constructed + 3 */ +#define LDAP_TAG_NEWSUPERIOR 0x80L /* context specific + primitive + 0 */ +#define LDAP_TAG_MRA_OID 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_MRA_TYPE 0x82L /* context specific + primitive + 2 */ +#define LDAP_TAG_MRA_VALUE 0x83L /* context specific + primitive + 3 */ +#define LDAP_TAG_MRA_DNATTRS 0x84L /* context specific + primitive + 4 */ +#define LDAP_TAG_EXOP_REQ_OID 0x80L /* context specific + primitive + 0 */ +#define LDAP_TAG_EXOP_REQ_VALUE 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_EXOP_RES_OID 0x8aL /* context specific + primitive + 10 */ +#define LDAP_TAG_EXOP_RES_VALUE 0x8bL /* context specific + primitive + 11 */ +#define LDAP_TAG_SK_MATCHRULE 0x80L /* context specific + primitive + 0 */ +#define LDAP_TAG_SK_REVERSE 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_SR_ATTRTYPE 0x80L /* context specific + primitive + 0 */ +#define LDAP_TAG_SASL_RES_CREDS 0x87L /* context specific + primitive + 7 */ +#define LDAP_TAG_VLV_BY_INDEX 0xa0L /* context specific + constructed + 0 */ +#define LDAP_TAG_VLV_BY_VALUE 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_PWP_WARNING 0xA0L /* context specific + constructed */ +#define LDAP_TAG_PWP_SECSLEFT 0x80L /* context specific + primitive */ +#define LDAP_TAG_PWP_GRCLOGINS 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_PWP_ERROR 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_PWDMOD_REQ_ID 0x80L /* context specific + primitive + 0 */ +#define LDAP_TAG_PWDMOD_REQ_OLD 0x81L /* context specific + primitive + 1 */ +#define LDAP_TAG_PWDMOD_REQ_NEW 0x82L /* context specific + primitive + 2 */ +#define LDAP_TAG_PWDMOD_RES_GEN 0x80L /* context specific + primitive + 0 */ + +/* possible operations a client can invoke */ +#define LDAP_REQ_BIND 0x60L /* application + constructed + 0 */ +#define LDAP_REQ_UNBIND 0x42L /* application + primitive + 2 */ +#define LDAP_REQ_SEARCH 0x63L /* application + constructed + 3 */ +#define LDAP_REQ_MODIFY 0x66L /* application + constructed + 6 */ +#define LDAP_REQ_ADD 0x68L /* application + constructed + 8 */ +#define LDAP_REQ_DELETE 0x4aL /* application + primitive + 10 */ +#define LDAP_REQ_MODRDN 0x6cL /* application + constructed + 12 */ +#define LDAP_REQ_MODDN 0x6cL /* application + constructed + 12 */ +#define LDAP_REQ_RENAME 0x6cL /* application + constructed + 12 */ +#define LDAP_REQ_COMPARE 0x6eL /* application + constructed + 14 */ +#define LDAP_REQ_ABANDON 0x50L /* application + primitive + 16 */ +#define LDAP_REQ_EXTENDED 0x77L /* application + constructed + 23 */ + +/* U-M LDAP release 3.0 compatibility stuff */ +#define LDAP_REQ_UNBIND_30 0x62L +#define LDAP_REQ_DELETE_30 0x6aL +#define LDAP_REQ_ABANDON_30 0x70L + +/* + * old broken stuff for backwards compatibility - forgot application tag + * and constructed/primitive bit + */ +#define OLD_LDAP_REQ_BIND 0x00L +#define OLD_LDAP_REQ_UNBIND 0x02L +#define OLD_LDAP_REQ_SEARCH 0x03L +#define OLD_LDAP_REQ_MODIFY 0x06L +#define OLD_LDAP_REQ_ADD 0x08L +#define OLD_LDAP_REQ_DELETE 0x0aL +#define OLD_LDAP_REQ_MODRDN 0x0cL +#define OLD_LDAP_REQ_MODDN 0x0cL +#define OLD_LDAP_REQ_COMPARE 0x0eL +#define OLD_LDAP_REQ_ABANDON 0x10L + +/* old broken stuff for backwards compatibility */ +#define OLD_LDAP_RES_BIND 0x01L +#define OLD_LDAP_RES_SEARCH_ENTRY 0x04L +#define OLD_LDAP_RES_SEARCH_RESULT 0x05L +#define OLD_LDAP_RES_MODIFY 0x07L +#define OLD_LDAP_RES_ADD 0x09L +#define OLD_LDAP_RES_DELETE 0x0bL +#define OLD_LDAP_RES_MODRDN 0x0dL +#define OLD_LDAP_RES_MODDN 0x0dL +#define OLD_LDAP_RES_COMPARE 0x0fL + +/* U-M LDAP 3.0 compatibility auth methods */ +#define LDAP_AUTH_SIMPLE_30 0xa0L /* context specific + constructed */ +#define LDAP_AUTH_KRBV41_30 0xa1L /* context specific + constructed */ +#define LDAP_AUTH_KRBV42_30 0xa2L /* context specific + constructed */ + +/* old broken stuff */ +#define OLD_LDAP_AUTH_SIMPLE 0x00L +#define OLD_LDAP_AUTH_KRBV4 0x01L +#define OLD_LDAP_AUTH_KRBV42 0x02L + +/* U-M LDAP 3.0 compatibility filter types */ +#define LDAP_FILTER_PRESENT_30 0xa7L /* context specific + constructed */ + +/* filter types */ +#define LDAP_FILTER_AND 0xa0L /* context specific + constructed + 0 */ +#define LDAP_FILTER_OR 0xa1L /* context specific + constructed + 1 */ +#define LDAP_FILTER_NOT 0xa2L /* context specific + constructed + 2 */ +#define LDAP_FILTER_EQUALITY 0xa3L /* context specific + constructed + 3 */ +#define LDAP_FILTER_SUBSTRINGS 0xa4L /* context specific + constructed + 4 */ +#define LDAP_FILTER_GE 0xa5L /* context specific + constructed + 5 */ +#define LDAP_FILTER_LE 0xa6L /* context specific + constructed + 6 */ +#define LDAP_FILTER_PRESENT 0x87L /* context specific + primitive + 7 */ +#define LDAP_FILTER_APPROX 0xa8L /* context specific + constructed + 8 */ +#define LDAP_FILTER_EXTENDED 0xa9L /* context specific + constructed + 0 */ + +/* old broken stuff */ +#define OLD_LDAP_FILTER_AND 0x00L +#define OLD_LDAP_FILTER_OR 0x01L +#define OLD_LDAP_FILTER_NOT 0x02L +#define OLD_LDAP_FILTER_EQUALITY 0x03L +#define OLD_LDAP_FILTER_SUBSTRINGS 0x04L +#define OLD_LDAP_FILTER_GE 0x05L +#define OLD_LDAP_FILTER_LE 0x06L +#define OLD_LDAP_FILTER_PRESENT 0x07L +#define OLD_LDAP_FILTER_APPROX 0x08L + +/* substring filter component types */ +#define LDAP_SUBSTRING_INITIAL 0x80L /* context specific + primitive + 0 */ +#define LDAP_SUBSTRING_ANY 0x81L /* context specific + primitive + 1 */ +#define LDAP_SUBSTRING_FINAL 0x82L /* context specific + primitive + 2 */ + +/* extended filter component types */ +#define LDAP_FILTER_EXTENDED_OID 0x81L /* context spec. + prim. + 1 */ +#define LDAP_FILTER_EXTENDED_TYPE 0x82L /* context spec. + prim. + 2 */ +#define LDAP_FILTER_EXTENDED_VALUE 0x83L /* context spec. + prim. + 3 */ +#define LDAP_FILTER_EXTENDED_DNATTRS 0x84L /* context spec. + prim. + 4 */ + +/* U-M LDAP 3.0 compatibility substring filter component types */ +#define LDAP_SUBSTRING_INITIAL_30 0xa0L /* context specific */ +#define LDAP_SUBSTRING_ANY_30 0xa1L /* context specific */ +#define LDAP_SUBSTRING_FINAL_30 0xa2L /* context specific */ + +/* old broken stuff */ +#define OLD_LDAP_SUBSTRING_INITIAL 0x00L +#define OLD_LDAP_SUBSTRING_ANY 0x01L +#define OLD_LDAP_SUBSTRING_FINAL 0x02L + +#ifdef __cplusplus +} +#endif +#endif /* _LDAPROT_H */ diff --git a/ldap/c-sdk/include/ldif.h b/ldap/c-sdk/include/ldif.h new file mode 100644 index 0000000000..87ce19e948 --- /dev/null +++ b/ldap/c-sdk/include/ldif.h @@ -0,0 +1,114 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* NOTE: As of mozldap version 6.0.1 the LDIF functions are now + publicly usable. The LDIF functions were originally designed for + "internal use only" purposes and as such the APIs are not very modern + or safe. For example, the caller needs to be careful to provide + adequately sized buffers and so on. +*/ + +#ifndef _LDIF_H +#define _LDIF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LDIF_VERSION_ONE 1 /* LDIF standard version */ + +#define LDIF_MAX_LINE_WIDTH 76 /* maximum length of LDIF lines */ + +/* + * Macro to calculate maximum number of bytes that the base64 equivalent + * of an item that is "vlen" bytes long will take up. Base64 encoding + * uses one byte for every six bits in the value plus up to two pad bytes. + */ +#define LDIF_BASE64_LEN(vlen) (((vlen) * 4 / 3 ) + 3) + +/* + * Macro to calculate maximum size that an LDIF-encoded type (length + * tlen) and value (length vlen) will take up: room for type + ":: " + + * first newline + base64 value + continued lines. Each continued line + * needs room for a newline and a leading space character. + */ +#define LDIF_SIZE_NEEDED(tlen,vlen) \ + ((tlen) + 4 + LDIF_BASE64_LEN(vlen) \ + + ((LDIF_BASE64_LEN(vlen) + tlen + 3) / LDIF_MAX_LINE_WIDTH * 2 )) + +/* + * Options for ldif_put_type_and_value_with_options() and + * ldif_type_and_value_with_options(). + */ +#define LDIF_OPT_NOWRAP 0x01UL +#define LDIF_OPT_VALUE_IS_URL 0x02UL +#define LDIF_OPT_MINIMAL_ENCODING 0x04UL + +int ldif_parse_line( char *line, char **type, char **value, int *vlen); +char * ldif_getline( char **next ); +void ldif_put_type_and_value( char **out, char *t, char *val, int vlen ); +void ldif_put_type_and_value_nowrap( char **out, char *t, char *val, int vlen ); +void ldif_put_type_and_value_with_options( char **out, char *t, char *val, + int vlen, unsigned long options ); +char *ldif_type_and_value( char *type, char *val, int vlen ); +char *ldif_type_and_value_nowrap( char *type, char *val, int vlen ); +char *ldif_type_and_value_with_options( char *type, char *val, int vlen, + unsigned long options ); +int ldif_base64_decode( char *src, unsigned char *dst ); +int ldif_base64_encode( unsigned char *src, char *dst, int srclen, + int lenused ); +int ldif_base64_encode_nowrap( unsigned char *src, char *dst, int srclen, + int lenused ); +char *ldif_get_entry( FILE *fp, int *lineno ); + +#ifdef __cplusplus +} +#endif + +#endif /* _LDIF_H */ diff --git a/ldap/c-sdk/include/portable.h b/ldap/c-sdk/include/portable.h new file mode 100644 index 0000000000..52698867f6 --- /dev/null +++ b/ldap/c-sdk/include/portable.h @@ -0,0 +1,462 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _PORTABLE_H +#define _PORTABLE_H + +/* + * portable.h for LDAP -- this is where we define common stuff to make + * life easier on various Unix systems. + * + * Unless you are porting LDAP to a new platform, you should not need to + * edit this file. + */ + +#ifndef SYSV +#if defined( hpux ) || defined( SOLARIS ) || defined ( sgi ) || defined( SVR4 ) +#define SYSV +#endif +#endif + +/* + * under System V, use sysconf() instead of getdtablesize + */ +#if !defined( USE_SYSCONF ) && defined( SYSV ) +#define USE_SYSCONF +#endif + +/* + * under System V, daemons should use setsid() instead of detaching from their + * tty themselves + */ +#if !defined( USE_SETSID ) && defined( SYSV ) +#define USE_SETSID +#endif + +/* + * System V has socket options in filio.h + */ +#if !defined( NEED_FILIO ) && defined( SYSV ) && !defined( hpux ) && !defined( AIX ) +#define NEED_FILIO +#endif + +/* + * use lockf() under System V + */ +#if !defined( USE_LOCKF ) && ( defined( SYSV ) || defined( aix )) +#define USE_LOCKF +#endif + +/* + * on many systems, we should use waitpid() instead of waitN() + */ +#if !defined( USE_WAITPID ) && ( defined( SYSV ) || defined( sunos4 ) || defined( ultrix ) || defined( aix )) +#define USE_WAITPID +#endif + +/* + * define the wait status argument type + */ +#if ( defined( SunOS ) && SunOS < 40 ) || defined( nextstep ) +#define WAITSTATUSTYPE union wait +#else +#define WAITSTATUSTYPE int +#endif + +/* + * defined the options for openlog (syslog) + */ +#ifdef ultrix +#define OPENLOG_OPTIONS LOG_PID +#else +#define OPENLOG_OPTIONS ( LOG_PID | LOG_NOWAIT ) +#endif + +/* + * some systems don't have the BSD re_comp and re_exec routines + */ +#ifndef NEED_BSDREGEX +#if ( defined( SYSV ) || defined( NETBSD ) || defined( FREEBSD ) || defined(__OpenBSD__) || defined( linux ) || defined( DARWIN )) && !defined(sgi) +#define NEED_BSDREGEX +#endif +#endif + +/* + * many systems do not have the setpwfile() library routine... we just + * enable use for those systems we know have it. + */ +#ifndef HAVE_SETPWFILE +#if defined( sunos4 ) || defined( ultrix ) || defined( OSF1 ) +#define HAVE_SETPWFILE +#endif +#endif + +/* + * Are sys_errlist and sys_nerr declared in stdio.h? + */ +#ifndef SYSERRLIST_IN_STDIO +#if defined( freebsd ) +#define SYSERRLIST_IN_STDIO +#endif +#endif + + +/* + * Is snprintf() part of the standard C runtime library? + */ +#if defined(_WINDOWS) +#define snprintf _snprintf +#endif + + +/* + * Async IO. Use a non blocking implementation of connect() and + * dns functions + */ +#if !defined(LDAP_ASYNC_IO) +#if !defined(_WINDOWS) && !defined(macintosh) +#define LDAP_ASYNC_IO +#endif /* _WINDOWS */ +#endif + +/* + * for select() + */ +#if !defined(WINSOCK) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2) +#if defined(hpux) || defined(LINUX) || defined(SUNOS4) || defined(XP_BEOS) +#include +#else +#include +#endif +#if !defined(FD_SET) +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) +#endif /* !FD_SET */ +#endif /* !WINSOCK && !_WINDOWS && !macintosh */ + + +/* + * for connect() -- must we block signals when calling connect()? This + * is necessary on some buggy UNIXes. + */ +#if !defined(NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED) && \ + ( defined(AIX) || defined(IRIX) || defined(HPUX) || defined(SUNOS4) \ + || defined(SOLARIS) || defined(OSF1) ||defined(freebsd)) +#define NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +#endif + +/* + * On most platforms, sigprocmask() works fine even in multithreaded code. + * But not everywhere. + */ +#ifdef AIX +#define NSLDAPI_MT_SAFE_SIGPROCMASK(h,s,o) sigthreadmask(h,s,o) +#else +#define NSLDAPI_MT_SAFE_SIGPROCMASK(h,s,o) sigprocmask(h,s,o) +#endif + +/* + * toupper and tolower macros are different under bsd and sys v + */ +#if defined( SYSV ) && !defined( hpux ) +#define TOUPPER(c) (isascii(c) && islower(c) ? _toupper(c) : c) +#define TOLOWER(c) (isascii(c) && isupper(c) ? _tolower(c) : c) +#else +#define TOUPPER(c) (isascii(c) && islower(c) ? toupper(c) : c) +#define TOLOWER(c) (isascii(c) && isupper(c) ? tolower(c) : c) +#endif + +/* + * put a cover on the tty-related ioctl calls we need to use + */ +#if defined( NeXT ) || (defined(SunOS) && SunOS < 40) +#define TERMIO_TYPE struct sgttyb +#define TERMFLAG_TYPE int +#define GETATTR( fd, tiop ) ioctl((fd), TIOCGETP, (caddr_t)(tiop)) +#define SETATTR( fd, tiop ) ioctl((fd), TIOCSETP, (caddr_t)(tiop)) +#define GETFLAGS( tio ) (tio).sg_flags +#define SETFLAGS( tio, flags ) (tio).sg_flags = (flags) +#else +#define USE_TERMIOS +#define TERMIO_TYPE struct termios +#define TERMFLAG_TYPE tcflag_t +#define GETATTR( fd, tiop ) tcgetattr((fd), (tiop)) +#define SETATTR( fd, tiop ) tcsetattr((fd), TCSANOW /* 0 */, (tiop)) +#define GETFLAGS( tio ) (tio).c_lflag +#define SETFLAGS( tio, flags ) (tio).c_lflag = (flags) +#endif + +#if ( !defined( HPUX9 )) && ( !defined( sunos4 )) && ( !defined( SNI )) && \ + ( !defined( HAVE_TIME_R )) +#define HAVE_TIME_R +#endif + +#if defined(SNI) || defined(LINUX1_2) +int strcasecmp(const char *, const char *); +#ifdef SNI +int strncasecmp(const char *, const char *, int); +#endif /* SNI */ +#ifdef LINUX1_2 +int strncasecmp(const char *, const char *, size_t); +#endif /* LINUX1_2 */ +#endif /* SNI || LINUX1_2 */ + +#if defined(_WINDOWS) || defined(macintosh) || defined(XP_OS2) || defined(DARWIN) +#define GETHOSTBYNAME( n, r, b, l, e ) gethostbyname( n ) +#define NSLDAPI_CTIME( c, b, l ) ctime( c ) +#define STRTOK( s1, s2, l ) strtok( s1, s2 ) +#elif defined(XP_BEOS) +#define GETHOSTBYNAME( n, r, b, l, e ) gethostbyname( n ) +#define NSLDAPI_CTIME( c, b, l ) ctime_r( c, b ) +#define STRTOK( s1, s2, l ) strtok_r( s1, s2, l ) +#define HAVE_STRTOK_R +#else /* UNIX */ +#if (defined(AIX) && defined(_THREAD_SAFE)) || defined(OSF1) +#define NSLDAPI_NETDB_BUF_SIZE sizeof(struct protoent_data) +#else +#define NSLDAPI_NETDB_BUF_SIZE 1024 +#endif + +#if defined(sgi) || defined(HPUX9) || defined(SCOOS) || \ + defined(UNIXWARE) || defined(SUNOS4) || defined(SNI) || defined(BSDI) || \ + defined(NCR) || defined(OSF1) || defined(NEC) || defined(VMS) || \ + ( defined(HPUX10) && !defined(_REENTRANT)) || defined(HPUX11) || \ + defined(UnixWare) || defined(NETBSD) || \ + defined(FREEBSD) || defined(OPENBSD) || \ + (defined(LINUX) && __GLIBC__ < 2) || \ + (defined(AIX) && !defined(USE_REENTRANT_LIBC)) +#define GETHOSTBYNAME( n, r, b, l, e ) gethostbyname( n ) +#elif defined(AIX) +/* Maybe this is for another version of AIX? + Commenting out for AIX 4.1 for Nova + Replaced with following to lines, stolen from the #else below +#define GETHOSTBYNAME_BUF_T struct hostent_data +*/ +typedef char GETHOSTBYNAME_buf_t [NSLDAPI_NETDB_BUF_SIZE]; +#define GETHOSTBYNAME_BUF_T GETHOSTBYNAME_buf_t +#define GETHOSTBYNAME( n, r, b, l, e ) \ + (memset (&b, 0, l), gethostbyname_r (n, r, &b) ? NULL : r) +#elif defined(HPUX10) +#define GETHOSTBYNAME_BUF_T struct hostent_data +#define GETHOSTBYNAME( n, r, b, l, e ) nsldapi_compat_gethostbyname_r( n, r, (char *)&b, l, e ) +#elif defined(LINUX) || defined(DRAGONFLY) +typedef char GETHOSTBYNAME_buf_t [NSLDAPI_NETDB_BUF_SIZE]; +#define GETHOSTBYNAME_BUF_T GETHOSTBYNAME_buf_t +#define GETHOSTBYNAME( n, r, b, l, rp, e ) gethostbyname_r( n, r, b, l, rp, e ) +#define GETHOSTBYNAME_R_RETURNS_INT +#else +typedef char GETHOSTBYNAME_buf_t [NSLDAPI_NETDB_BUF_SIZE]; +#define GETHOSTBYNAME_BUF_T GETHOSTBYNAME_buf_t +#define GETHOSTBYNAME( n, r, b, l, e ) gethostbyname_r( n, r, b, l, e ) +#endif +#if defined(HPUX9) || defined(LINUX1_2) || defined(LINUX2_0) || \ + defined(LINUX2_1) || defined(SUNOS4) || defined(SNI) || \ + defined(SCOOS) || defined(BSDI) || defined(NCR) || \ + defined(NEC) || ( defined(HPUX10) && !defined(_REENTRANT)) || \ + (defined(AIX) && !defined(USE_REENTRANT_LIBC)) +#define NSLDAPI_CTIME( c, b, l ) ctime( c ) +#elif defined(HPUX10) && defined(_REENTRANT) && !defined(HPUX11) +#define NSLDAPI_CTIME( c, b, l ) nsldapi_compat_ctime_r( c, b, l ) +#elif defined( IRIX6_2 ) || defined( IRIX6_3 ) || defined(UNIXWARE) \ + || defined(OSF1V4) || defined(AIX) || defined(UnixWare) \ + || defined(hpux) || defined(HPUX11) || defined(NETBSD) \ + || defined(IRIX6) || defined(FREEBSD) || defined(VMS) \ + || defined(NTO) || defined(OPENBSD) || defined(DRAGONFLY) +#define NSLDAPI_CTIME( c, b, l ) ctime_r( c, b ) +#elif defined( OSF1V3 ) +#define NSLDAPI_CTIME( c, b, l ) (ctime_r( c, b, l ) ? NULL : b) +#else +#define NSLDAPI_CTIME( c, b, l ) ctime_r( c, b, l ) +#endif +#if defined(hpux9) || defined(SUNOS4) || defined(SNI) || \ + defined(SCOOS) || defined(BSDI) || defined(NCR) || defined(VMS) || \ + defined(NEC) || (defined(LINUX) && __GNU_LIBRARY__ != 6) || \ + (defined(AIX) && !defined(USE_REENTRANT_LIBC)) +#define STRTOK( s1, s2, l ) strtok( s1, s2 ) +#else +#define HAVE_STRTOK_R +#ifndef strtok_r +char *strtok_r(char *, const char *, char **); +#endif +#define STRTOK( s1, s2, l ) (char *)strtok_r( s1, s2, l ) +#endif /* STRTOK */ +#endif /* UNIX */ + +#if defined( ultrix ) || defined( nextstep ) +extern char *strdup(); +#endif /* ultrix || nextstep */ + +#if defined( sunos4 ) || defined( OSF1 ) +#define BSD_TIME 1 /* for servers/slapd/log.h */ +#endif /* sunos4 || osf */ + +#if defined(XP_OS2) +#include /* for htonl, et.al. */ +#include /* for inet_addr() */ +#elif !defined(_WINDOWS) && !defined(macintosh) +#include +#if !defined(XP_BEOS) +#include /* for inet_addr() */ +#endif +#endif + + +/* + * Define portable 32-bit integral types. + */ +#include +#if UINT_MAX >= 0xffffffffU /* an int holds at least 32 bits */ + typedef signed int nsldapi_int_32; + typedef unsigned int nsldapi_uint_32; +#else /* ints are < 32 bits; use long instead */ + typedef signed long nsldapi_int_32; + typedef unsigned long nsldapi_uint_32; +#endif + +/* + * Define a portable type for IPv4 style Internet addresses (32 bits): + */ +#if defined(_IN_ADDR_T) || defined(aix) || defined(HPUX11) || defined(OSF1) +typedef in_addr_t nsldapi_in_addr_t; +#else +typedef nsldapi_uint_32 nsldapi_in_addr_t; +#endif + +#ifdef SUNOS4 +#include /* for toupper() */ +int fprintf(FILE *, char *, ...); +int fseek(FILE *, long, int); +int fread(char *, int, int, FILE *); +int fclose(FILE *); +int fflush(FILE *); +int rewind(FILE *); +void *memmove(void *, const void *, size_t); +int strcasecmp(char *, char *); +int strncasecmp(char *, char *, int); +time_t time(time_t *); +void perror(char *); +int fputc(char, FILE *); +int fputs(char *, FILE *); +int re_exec(char *); +int socket(int, int, int); +void bzero(char *, int); +unsigned long inet_addr(char *); +char * inet_ntoa(struct in_addr); +int getdtablesize(); +int connect(int, struct sockaddr *, int); +#endif /* SUNOS4 */ + +/* #if defined(SUNOS4) || defined(SNI) */ +#if defined(SUNOS4) +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +#endif /* SUNOS4 || SNI */ + +/* + * SAFEMEMCPY is an overlap-safe copy from s to d of n bytes + */ +#ifdef macintosh +#define SAFEMEMCPY( d, s, n ) BlockMoveData( (Ptr)s, (Ptr)d, n ) +#else /* macintosh */ +#ifdef sunos4 +#define SAFEMEMCPY( d, s, n ) bcopy( s, d, n ) +#else /* sunos4 */ +#define SAFEMEMCPY( d, s, n ) memmove( d, s, n ) +#endif /* sunos4 */ +#endif /* macintosh */ + +#ifdef _WINDOWS + +#define strcasecmp strcmpi +#define strncasecmp _strnicmp +#define bzero(a, b) memset( a, 0, b ) +#define getpid _getpid +#define ioctl ioctlsocket +#define sleep(a) Sleep( a*1000 ) + +#define EMSGSIZE WSAEMSGSIZE +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EHOSTUNREACH WSAEHOSTUNREACH + +#ifndef MAXPATHLEN +#define MAXPATHLEN _MAX_PATH +#endif + +/* We'd like this number to be prime for the hash + * into the Connection table */ +#define DS_MAX_NT_SOCKET_CONNECTIONS 2003 + +#elif defined(XP_OS2) + +#define strcasecmp stricmp +#define strncasecmp strnicmp +#define bzero(a, b) memset( a, 0, b ) +#include /*for strcmpi()*/ +#include /*for ctime()*/ + +#endif /* XP_OS2 */ + +/* Define a macro to support large files */ +#ifdef _LARGEFILE64_SOURCE +#define NSLDAPI_FOPEN( filename, mode ) fopen64( filename, mode ) +#else +#define NSLDAPI_FOPEN( filename, mode ) fopen( filename, mode ) +#endif + +#if defined(LINUX) || defined(AIX) || defined(HPUX) || defined(_WINDOWS) +size_t nsldapi_compat_strlcpy(char *dst, const char *src, size_t len); +#define STRLCPY nsldapi_compat_strlcpy +#else +#define STRLCPY strlcpy +#endif + +#endif /* _PORTABLE_H */ diff --git a/ldap/c-sdk/include/proto-ntutil.h b/ldap/c-sdk/include/proto-ntutil.h new file mode 100644 index 0000000000..616f468974 --- /dev/null +++ b/ldap/c-sdk/include/proto-ntutil.h @@ -0,0 +1,99 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/****************************************************** + * + * proto-ntutil.h - Prototypes for utility functions used + * throughout slapd on NT. + * + ******************************************************/ +#if defined( _WINDOWS ) + +#ifndef _PROTO_NTUTIL +#define _PROTO_NTUTIL + +/* + * + * ntreg.c + * + */ +extern int SlapdGetRegSZ( LPTSTR lpszRegKey, LPSTR lpszValueName, LPTSTR lpszValue ); + +/* + * + * getopt.c + * + */ +extern int getopt (int argc, char *const *argv, const char *optstring); + +/* + * + * ntevent.c + * + */ +extern BOOL MultipleInstances(); +extern BOOL SlapdIsAService(); +extern void InitializeSlapdLogging( LPTSTR lpszRegLocation, LPTSTR lpszEventLogName, LPTSTR lpszMessageFile ); +extern void ReportSlapdEvent(WORD wEventType, DWORD dwIdEvent, WORD wNumInsertStrings, + char *pszStrings); +extern BOOL ReportSlapdStatusToSCMgr( + SERVICE_STATUS *serviceStatus, + SERVICE_STATUS_HANDLE serviceStatusHandle, + HANDLE Event, + DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwCheckPoint, + DWORD dwWaitHint); +extern void WINAPI SlapdServiceCtrlHandler(DWORD dwOpcode); +extern BOOL SlapdGetServerNameFromCmdline(char *szServerName, char *szCmdLine); + +/* + * + * ntgetpassword.c + * + */ +#ifdef NET_SSL +extern char *Slapd_GetPassword(); +#ifdef FORTEZZA +extern char *Slapd_GetFortezzaPIN(); +#endif +extern void CenterDialog(HWND hwndParent, HWND hwndDialog); +#endif /* NET_SSL */ + +#endif /* _PROTO_NTUTIL */ + +#endif /* _WINDOWS */ diff --git a/ldap/c-sdk/include/regex.h b/ldap/c-sdk/include/regex.h new file mode 100644 index 0000000000..8d2c609625 --- /dev/null +++ b/ldap/c-sdk/include/regex.h @@ -0,0 +1,95 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if defined( macintosh ) || defined( DOS ) || defined( _WINDOWS ) || defined( NEED_BSDREGEX ) || defined( XP_OS2 ) +/* + * Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* + * regex.h -- includes for regular expression matching routines + * 13 August 1993 Mark C Smith + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ldap.h" + +#if !defined( NEEDPROTOS ) && defined( __STDC__ ) +#define NEEDPROTOS +#endif + +#ifdef _SLDAPD_H_ /* server build: no need to use LDAP_CALL stuff */ +#ifdef LDAP_CALL +#undef LDAP_CALL +#define LDAP_CALL +#endif +#endif + +#ifdef NEEDPROTOS +int re_init( void ); +void re_lock( void ); +int re_unlock( void ); +char * LDAP_CALL re_comp( const char *pat ); +int LDAP_CALL re_exec( const char *lp ); +void LDAP_CALL re_modw( char *s ); +int LDAP_CALL re_subs( char *src, char *dst ); +#else /* NEEDPROTOS */ +int re_init(); +void re_lock(); +int re_unlock(); +char * LDAP_CALL re_comp(); +int LDAP_CALL re_exec(); +void LDAP_CALL re_modw(); +int LDAP_CALL re_subs(); +#endif /* NEEDPROTOS */ + +#define re_fail( m, p ) + +#ifdef __cplusplus +} +#endif +#endif /* macintosh or DOS or or _WIN32 or NEED_BSDREGEX */ diff --git a/ldap/c-sdk/include/srchpref.h b/ldap/c-sdk/include/srchpref.h new file mode 100644 index 0000000000..1e28a5dc92 --- /dev/null +++ b/ldap/c-sdk/include/srchpref.h @@ -0,0 +1,154 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1993, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + * + * searchpref.h: display template library defines + */ + + +#ifndef _SRCHPREF_H +#define _SRCHPREF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* calling conventions used by library */ +#ifndef LDAP_CALL +#if defined( _WINDOWS ) || defined( _WIN32 ) +#define LDAP_C __cdecl +#ifndef _WIN32 +#define __stdcall _far _pascal +#define LDAP_CALLBACK _loadds +#else +#define LDAP_CALLBACK +#endif /* _WIN32 */ +#define LDAP_PASCAL __stdcall +#define LDAP_CALL LDAP_PASCAL +#else /* _WINDOWS */ +#define LDAP_C +#define LDAP_CALLBACK +#define LDAP_PASCAL +#define LDAP_CALL +#endif /* _WINDOWS */ +#endif /* LDAP_CALL */ + +struct ldap_searchattr { + char *sa_attrlabel; + char *sa_attr; + /* max 32 matchtypes for now */ + unsigned long sa_matchtypebitmap; + char *sa_selectattr; + char *sa_selecttext; + struct ldap_searchattr *sa_next; +}; + +struct ldap_searchmatch { + char *sm_matchprompt; + char *sm_filter; + struct ldap_searchmatch *sm_next; +}; + +struct ldap_searchobj { + char *so_objtypeprompt; + unsigned long so_options; + char *so_prompt; + short so_defaultscope; + char *so_filterprefix; + char *so_filtertag; + char *so_defaultselectattr; + char *so_defaultselecttext; + struct ldap_searchattr *so_salist; + struct ldap_searchmatch *so_smlist; + struct ldap_searchobj *so_next; +}; + +#define NULLSEARCHOBJ ((struct ldap_searchobj *)0) + +/* + * global search object options + */ +#define LDAP_SEARCHOBJ_OPT_INTERNAL 0x00000001 + +#define LDAP_IS_SEARCHOBJ_OPTION_SET( so, option ) \ + (((so)->so_options & option ) != 0 ) + +#define LDAP_SEARCHPREF_VERSION_ZERO 0 +#define LDAP_SEARCHPREF_VERSION 1 + +#define LDAP_SEARCHPREF_ERR_VERSION 1 +#define LDAP_SEARCHPREF_ERR_MEM 2 +#define LDAP_SEARCHPREF_ERR_SYNTAX 3 +#define LDAP_SEARCHPREF_ERR_FILE 4 + + +LDAP_API(int) +LDAP_CALL +ldap_init_searchprefs( char *file, struct ldap_searchobj **solistp ); + +LDAP_API(int) +LDAP_CALL +ldap_init_searchprefs_buf( char *buf, long buflen, + struct ldap_searchobj **solistp ); + +LDAP_API(void) +LDAP_CALL +ldap_free_searchprefs( struct ldap_searchobj *solist ); + +LDAP_API(struct ldap_searchobj *) +LDAP_CALL +ldap_first_searchobj( struct ldap_searchobj *solist ); + +LDAP_API(struct ldap_searchobj *) +LDAP_CALL +ldap_next_searchobj( struct ldap_searchobj *sollist, + struct ldap_searchobj *so ); + +#ifdef __cplusplus +} +#endif +#endif /* _SRCHPREF_H */ diff --git a/ldap/c-sdk/libraries/liblber/bprint.c b/ldap/c-sdk/libraries/liblber/bprint.c new file mode 100644 index 0000000000..7a7ccc01f2 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/bprint.c @@ -0,0 +1,102 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* bprint.c - a printing utility for debuging output */ +#include +#include "lber-int.h" + +#ifdef LDAP_DEBUG +/* + * Print arbitrary stuff, for debugging. + */ + +#define BPLEN 48 + +void +lber_bprint( char *data, int len ) +{ + static char hexdig[] = "0123456789abcdef"; + char out[ BPLEN ]; + int i = 0; + + memset( out, 0, BPLEN ); + for ( ;; ) { + if ( len < 1 ) { + char msg[BPLEN + 80]; + sprintf( msg, "\t%s\n", ( i == 0 ) ? "(end)" : out ); + ber_err_print( msg ); + break; + } + +#ifndef HEX + if ( isgraph( (unsigned char)*data )) { + out[ i ] = ' '; + out[ i+1 ] = *data; + } else { +#endif + out[ i ] = hexdig[ ( *data & 0xf0 ) >> 4 ]; + out[ i+1 ] = hexdig[ *data & 0x0f ]; +#ifndef HEX + } +#endif + i += 2; + len--; + data++; + + if ( i > BPLEN - 2 ) { + char msg[BPLEN + 80]; + sprintf( msg, "\t%s\n", out ); + ber_err_print( msg ); + memset( out, 0, BPLEN ); + i = 0; + continue; + } + out[ i++ ] = ' '; + } +} + +#endif + +void ber_err_print( char *data ) +{ +#ifdef USE_DEBUG_WIN + OutputDebugString( data ); +#else + fputs( data, stderr ); + fflush( stderr ); +#endif +} diff --git a/ldap/c-sdk/libraries/liblber/decode.c b/ldap/c-sdk/libraries/liblber/decode.c new file mode 100644 index 0000000000..732dd6e646 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/decode.c @@ -0,0 +1,833 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* decode.c - ber input decoding routines */ + +#include "lber-int.h" + +/* + * Note: ber_get_tag() only uses the ber_end and ber_ptr elements of ber. + * If that changes, the ber_peek_tag() and/or ber_skip_tag() implementations + * will need to be changed. + */ +/* return the tag - LBER_DEFAULT returned means trouble */ +ber_tag_t +LDAP_CALL +ber_get_tag( BerElement *ber ) +{ + unsigned char xbyte; + ber_tag_t tag; + char *tagp; + int i; + + if ( ber_read( ber, (char *) &xbyte, 1 ) != 1 ) + return( LBER_DEFAULT ); + + if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) + return( (ber_uint_t) xbyte ); + + tagp = (char *) &tag; + tagp[0] = xbyte; + for ( i = 1; i < sizeof(ber_int_t); i++ ) { + if ( ber_read( ber, (char *) &xbyte, 1 ) != 1 ) + return( LBER_DEFAULT ); + + tagp[i] = xbyte; + + if ( ! (xbyte & LBER_MORE_TAG_MASK) ) + break; + } + + /* tag too big! */ + if ( i == sizeof(ber_int_t) ) + return( LBER_DEFAULT ); + + /* want leading, not trailing 0's */ + return( tag >> (sizeof(ber_int_t) - i - 1) ); +} + +/* + * Note: ber_skip_tag() only uses the ber_end and ber_ptr elements of ber. + * If that changes, the implementation of ber_peek_tag() will need to + * be changed. + */ +ber_tag_t +LDAP_CALL +ber_skip_tag( BerElement *ber, ber_len_t *len ) +{ + ber_tag_t tag; + unsigned char lc; + int noctets, diff; + ber_len_t netlen; + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag byte (we + * can assume a single byte), and return the length in len. + * + * Assumptions: + * 1) definite lengths + * 2) primitive encodings used whenever possible + */ + + /* + * First, we read the tag. + */ + + if ( (tag = ber_get_tag( ber )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + + /* + * Next, read the length. The first byte contains the length of + * the length. If bit 8 is set, the length is the long form, + * otherwise it's the short form. We don't allow a length that's + * greater than what we can hold in an unsigned long. + */ + + *len = netlen = 0; + if ( ber_read( ber, (char *) &lc, 1 ) != 1 ) + return( LBER_DEFAULT ); + if ( lc & 0x80 ) { + noctets = (lc & 0x7f); + if ( noctets > sizeof(ber_uint_t) ) + return( LBER_DEFAULT ); + diff = sizeof(ber_int_t) - noctets; + if ( ber_read( ber, (char *) &netlen + diff, noctets ) + != noctets ) + return( LBER_DEFAULT ); + *len = LBER_NTOHL( netlen ); + } else { + *len = lc; + } + + return( tag ); +} + + +/* + * Note: Previously, we passed the "ber" parameter directly to ber_skip_tag(), + * saving and restoring the ber_ptr element only. We now take advantage + * of the fact that the only ber structure elements touched by ber_skip_tag() + * are ber_end and ber_ptr. If that changes, this code must change too. + */ +ber_tag_t +LDAP_CALL +ber_peek_tag( BerElement *ber, ber_len_t *len ) +{ + BerElement bercopy; + + bercopy.ber_end = ber->ber_end; + bercopy.ber_ptr = ber->ber_ptr; + return( ber_skip_tag( &bercopy, len )); +} + +static int +ber_getnint( BerElement *ber, ber_int_t *num, ber_slen_t len ) +{ + int i; + ber_int_t value; + unsigned char buffer[sizeof(ber_int_t)]; + /* + * The tag and length have already been stripped off. We should + * be sitting right before len bytes of 2's complement integer, + * ready to be read straight into an int. We may have to sign + * extend after we read it in. + */ + + if ( len > sizeof(ber_slen_t) ) + return( -1 ); + + /* read into the low-order bytes of netnum */ + if ( ber_read( ber, (char *) buffer, len ) != len ) + return( -1 ); + + /* This sets the required sign extension */ + if ( len != 0) { + value = 0x80 & buffer[0] ? (-1) : 0; + } else { + value = 0; + } + + for ( i = 0; i < len; i++ ) + value = (value << 8) | buffer[i]; + + *num = value; + + return( len ); +} + +ber_tag_t +LDAP_CALL +ber_get_int( BerElement *ber, ber_int_t *num ) +{ + ber_tag_t tag; + ber_len_t len; + + if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + + /* + * len is being demoted to a long here -- possible conversion error + */ + + if ( ber_getnint( ber, num, (int)len ) != (ber_slen_t)len ) + return( LBER_DEFAULT ); + else + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_stringb( BerElement *ber, char *buf, ber_len_t *len ) +{ + ber_len_t datalen; + ber_tag_t tag; +#ifdef STR_TRANSLATION + char *transbuf; +#endif /* STR_TRANSLATION */ + + if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + if ( datalen > (*len - 1) ) + return( LBER_DEFAULT ); + + /* + * datalen is being demoted to a long here -- possible conversion error + */ + + if ( ber_read( ber, buf, datalen ) != (ber_slen_t) datalen ) + return( LBER_DEFAULT ); + + buf[datalen] = '\0'; + +#ifdef STR_TRANSLATION + if ( datalen > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) + != 0 && ber->ber_decode_translate_proc != NULL ) { + transbuf = buf; + ++datalen; + if ( (*(ber->ber_decode_translate_proc))( &transbuf, &datalen, + 0 ) != 0 ) { + return( LBER_DEFAULT ); + } + if ( datalen > *len ) { + NSLBERI_FREE( transbuf ); + return( LBER_DEFAULT ); + } + SAFEMEMCPY( buf, transbuf, datalen ); + NSLBERI_FREE( transbuf ); + --datalen; + } +#endif /* STR_TRANSLATION */ + + *len = datalen; + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_stringa( BerElement *ber, char **buf ) +{ + ber_len_t datalen, ndatalen; + ber_tag_t tag; + + if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + + if ( ((ndatalen = (size_t)datalen + 1) < (size_t) datalen) || + ( datalen > (ber->ber_end - ber->ber_ptr) ) || + ( (*buf = (char *)NSLBERI_MALLOC( (size_t)ndatalen )) == NULL )) + return( LBER_DEFAULT ); + + /* + * datalen is being demoted to a long here -- possible conversion error + */ + if ( ber_read( ber, *buf, datalen ) != (ber_slen_t) datalen ) { + NSLBERI_FREE( *buf ); + *buf = NULL; + return( LBER_DEFAULT ); + } + + (*buf)[datalen] = '\0'; + +#ifdef STR_TRANSLATION + if ( datalen > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) + != 0 && ber->ber_decode_translate_proc != NULL ) { + ++datalen; + if ( (*(ber->ber_decode_translate_proc))( buf, &datalen, 1 ) + != 0 ) { + NSLBERI_FREE( *buf ); + *buf = NULL; + return( LBER_DEFAULT ); + } + } +#endif /* STR_TRANSLATION */ + + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_stringal( BerElement *ber, struct berval **bv ) +{ + ber_len_t len, nlen; + ber_tag_t tag; + + if ( (*bv = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) )) + == NULL ) { + return( LBER_DEFAULT ); + } + + (*bv)->bv_val = NULL; + (*bv)->bv_len = 0; + + if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) { + NSLBERI_FREE( *bv ); + *bv = NULL; + return( LBER_DEFAULT ); + } + + if ( ((nlen = (size_t) len + 1) < (size_t)len) || + ( len > (ber->ber_end - ber->ber_ptr) ) || + (((*bv)->bv_val = (char *)NSLBERI_MALLOC( (size_t)nlen )) + == NULL )) { + NSLBERI_FREE( *bv ); + *bv = NULL; + return( LBER_DEFAULT ); + } + + /* + * len is being demoted to a long here -- possible conversion error + */ + if ( ber_read( ber, (*bv)->bv_val, len ) != (ber_slen_t) len ) { + NSLBERI_FREE( (*bv)->bv_val ); + (*bv)->bv_val = NULL; + NSLBERI_FREE( *bv ); + *bv = NULL; + return( LBER_DEFAULT ); + } + + ((*bv)->bv_val)[len] = '\0'; + (*bv)->bv_len = len; + +#ifdef STR_TRANSLATION + if ( len > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) != 0 + && ber->ber_decode_translate_proc != NULL ) { + ++len; + if ( (*(ber->ber_decode_translate_proc))( &((*bv)->bv_val), + &len, 1 ) != 0 ) { + NSLBERI_FREE( (*bv)->bv_val ); + (*bv)->bv_val = NULL; + NSLBERI_FREE( *bv ); + *bv = NULL; + return( LBER_DEFAULT ); + } + (*bv)->bv_len = len - 1; + } +#endif /* STR_TRANSLATION */ + + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_bitstringa( BerElement *ber, char **buf, ber_len_t *blen ) +{ + ber_len_t datalen; + ber_tag_t tag; + unsigned char unusedbits; + + if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + --datalen; + + if ( (datalen > (ber->ber_end - ber->ber_ptr)) || + ( (*buf = (char *)NSLBERI_MALLOC((size_t)datalen )) == NULL ) ) + return( LBER_DEFAULT ); + + if ( ber_read( ber, (char *)&unusedbits, 1 ) != 1 ) { + NSLBERI_FREE( *buf ); + *buf = NULL; + return( LBER_DEFAULT ); + } + + /* + * datalen is being demoted to a long here -- possible conversion error + */ + if ( ber_read( ber, *buf, datalen ) != (ber_slen_t) datalen ) { + NSLBERI_FREE( *buf ); + *buf = NULL; + return( LBER_DEFAULT ); + } + + *blen = datalen * 8 - unusedbits; + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_null( BerElement *ber ) +{ + ber_len_t len; + ber_tag_t tag; + + if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) + return( LBER_DEFAULT ); + + if ( len != 0 ) + return( LBER_DEFAULT ); + + return( tag ); +} + +ber_tag_t +LDAP_CALL +ber_get_boolean( BerElement *ber, ber_int_t *boolval ) +{ + int rc; + + rc = ber_get_int( ber, boolval ); + + return( rc ); +} + +ber_tag_t +LDAP_CALL +ber_first_element( BerElement *ber, ber_len_t *len, char **last ) +{ + /* skip the sequence header, use the len to mark where to stop */ + if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) { + return( LBER_ERROR ); + } + + *last = ber->ber_ptr + *len; + + if ( *last == ber->ber_ptr ) { + return( LBER_END_OF_SEQORSET ); + } + + return( ber_peek_tag( ber, len ) ); +} + +ber_tag_t +LDAP_CALL +ber_next_element( BerElement *ber, ber_len_t *len, char *last ) +{ + if ( ber->ber_ptr == last ) { + return( LBER_END_OF_SEQORSET ); + } + + return( ber_peek_tag( ber, len ) ); +} + +/* VARARGS */ +ber_tag_t +LDAP_C +ber_scanf( BerElement *ber, const char *fmt, ... ) +{ + va_list ap; + char *last, *p; + char *s, **ss, ***sss; + struct berval ***bv, **bvp, *bval; + int *i, j; + ber_int_t *l, rc, tag; + ber_tag_t *t; + ber_len_t len; + size_t array_size; + + va_start( ap, fmt ); + +#ifdef LDAP_DEBUG + if ( lber_debug & 64 ) { + char msg[80]; + sprintf( msg, "ber_scanf fmt (%s) ber:\n", fmt ); + ber_err_print( msg ); + ber_dump( ber, 1 ); + } +#endif + for ( rc = 0, p = (char *) fmt; *p && rc != LBER_DEFAULT; p++ ) { + switch ( *p ) { + case 'a': /* octet string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa( ber, ss ); + break; + + case 'b': /* boolean */ + i = va_arg( ap, int * ); + rc = ber_get_boolean( ber, i ); + break; + + case 'e': /* enumerated */ + case 'i': /* int */ + l = va_arg( ap, ber_slen_t * ); + rc = ber_get_int( ber, l ); + break; + + case 'l': /* length of next item */ + l = va_arg( ap, ber_slen_t * ); + rc = ber_peek_tag( ber, (ber_len_t *)l ); + break; + + case 'n': /* null */ + rc = ber_get_null( ber ); + break; + + case 's': /* octet string - in a buffer */ + s = va_arg( ap, char * ); + l = va_arg( ap, ber_slen_t * ); + rc = ber_get_stringb( ber, s, (ber_len_t *)l ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + ber_peek_tag( ber, &bval->bv_len ); + rc = ber_get_stringa( ber, &bval->bv_val ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + rc = ber_get_stringal( ber, bvp ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + l = va_arg( ap, ber_slen_t * ); /* for length, in bits */ + rc = ber_get_bitstringa( ber, ss, (ber_len_t *)l ); + break; + + case 't': /* tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_peek_tag( ber, &len ); + break; + + case 'T': /* skip tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_skip_tag( ber, &len ); + break; + + case 'v': /* sequence of strings */ + sss = va_arg( ap, char *** ); + *sss = NULL; + j = 0; + array_size = 0; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET + && rc != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) { + if ( *sss == NULL ) { + /* Make room for at least 15 strings */ + *sss = (char **)NSLBERI_MALLOC(16 * sizeof(char *) ); + if (!*sss) { + rc = LBER_DEFAULT; + break; /* out of memory - cannot continue */ + } + array_size = 16; + } else { + char **save_sss = *sss; + if ( (size_t)(j+2) > array_size) { + /* We'v overflowed our buffer */ + *sss = (char **)NSLBERI_REALLOC( *sss, (array_size * 2) * sizeof(char *) ); + array_size = array_size * 2; + } + if (!*sss) { + rc = LBER_DEFAULT; + ber_svecfree(save_sss); + break; /* out of memory - cannot continue */ + } + } + (*sss)[j] = NULL; + rc = ber_get_stringa( ber, &((*sss)[j]) ); + j++; + } + if ( rc != LBER_DEFAULT && + tag != LBER_END_OF_SEQORSET ) { + rc = LBER_DEFAULT; + } + if ( *sss && (j > 0) ) { + (*sss)[j] = NULL; + } + break; + + case 'V': /* sequence of strings + lengths */ + bv = va_arg( ap, struct berval *** ); + *bv = NULL; + j = 0; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET + && rc != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) { + if ( *bv == NULL ) { + *bv = (struct berval **)NSLBERI_MALLOC( + 2 * sizeof(struct berval *) ); + if (!*bv) { + rc = LBER_DEFAULT; + break; /* out of memory - cannot continue */ + } + } else { + struct berval **save_bv = *bv; + *bv = (struct berval **)NSLBERI_REALLOC( + *bv, + (j + 2) * sizeof(struct berval *) ); + if (!*bv) { + rc = LBER_DEFAULT; + ber_bvecfree(save_bv); + break; /* out of memory - cannot continue */ + } + } + rc = ber_get_stringal( ber, &((*bv)[j]) ); + j++; + } + if ( rc != LBER_DEFAULT && + tag != LBER_END_OF_SEQORSET ) { + rc = LBER_DEFAULT; + } + if ( *bv && (j > 0) ) { + (*bv)[j] = NULL; + } + break; + + case 'x': /* skip the next element - whatever it is */ + if ( (rc = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) + break; + ber->ber_ptr += len; + break; + + case '{': /* begin sequence */ + case '[': /* begin set */ + if ( *(p + 1) != 'v' && *(p + 1) != 'V' ) + rc = ber_skip_tag( ber, &len ); + break; + + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + { + char msg[80]; + sprintf( msg, "unknown fmt %c\n", *p ); + ber_err_print( msg ); + } + rc = LBER_DEFAULT; + break; + } + } + + va_end( ap ); + + if (rc == LBER_DEFAULT) { + va_start( ap, fmt ); + for ( p--; fmt < p && *fmt; fmt++ ) { + switch ( *fmt ) { + case 'a': /* octet string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + NSLBERI_FREE(*ss); + *ss = NULL; + break; + + case 'b': /* boolean */ + i = va_arg( ap, int * ); + break; + + case 'e': /* enumerated */ + case 'i': /* int */ + l = va_arg( ap, ber_slen_t * ); + break; + + case 'l': /* length of next item */ + l = va_arg( ap, ber_slen_t * ); + break; + + case 'n': /* null */ + break; + + case 's': /* octet string - in a buffer */ + s = va_arg( ap, char * ); + l = va_arg( ap, ber_slen_t * ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + if (bval->bv_val) NSLBERI_FREE(bval->bv_val); + memset(bval, 0, sizeof(struct berval)); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + ber_bvfree(*bvp); + bvp = NULL; + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + l = va_arg( ap, ber_slen_t * ); /* for length, in bits */ + if (*ss) NSLBERI_FREE(*ss); + *ss = NULL; + break; + + case 't': /* tag of next item */ + t = va_arg( ap, ber_tag_t * ); + break; + + case 'T': /* skip tag of next item */ + t = va_arg( ap, ber_tag_t * ); + break; + + case 'v': /* sequence of strings */ + sss = va_arg( ap, char *** ); + ber_svecfree(*sss); + *sss = NULL; + break; + + case 'V': /* sequence of strings + lengths */ + bv = va_arg( ap, struct berval *** ); + ber_bvecfree(*bv); + *bv = NULL; + break; + + case 'x': /* skip the next element - whatever it is */ + break; + + case '{': /* begin sequence */ + case '[': /* begin set */ + break; + + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + break; + } + } /* for */ + va_end( ap ); + } /* if */ + + return( rc ); +} + +void +LDAP_CALL +ber_bvfree( struct berval *bv ) +{ + if ( bv != NULL ) { + if ( bv->bv_val != NULL ) { + NSLBERI_FREE( bv->bv_val ); + } + NSLBERI_FREE( (char *) bv ); + } +} + +void +LDAP_CALL +ber_bvecfree( struct berval **bv ) +{ + int i; + + if ( bv != NULL ) { + for ( i = 0; bv[i] != NULL; i++ ) { + ber_bvfree( bv[i] ); + } + NSLBERI_FREE( (char *) bv ); + } +} + +struct berval * +LDAP_CALL +ber_bvdup( const struct berval *bv ) +{ + struct berval *new; + + if ( (new = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) )) + == NULL ) { + return( NULL ); + } + if ( bv->bv_val == NULL ) { + new->bv_val = NULL; + new->bv_len = 0; + } else { + if ( (new->bv_val = (char *)NSLBERI_MALLOC( bv->bv_len + 1 )) + == NULL ) { + NSLBERI_FREE( new ); + new = NULL; + return( NULL ); + } + SAFEMEMCPY( new->bv_val, bv->bv_val, (size_t) bv->bv_len ); + new->bv_val[bv->bv_len] = '\0'; + new->bv_len = bv->bv_len; + } + + return( new ); +} + +void +LDAP_CALL +ber_svecfree( char **vals ) +{ + int i; + + if ( vals == NULL ) + return; + for ( i = 0; vals[i] != NULL; i++ ) + NSLBERI_FREE( vals[i] ); + NSLBERI_FREE( (char *) vals ); +} + +#ifdef STR_TRANSLATION +void +LDAP_CALL +ber_set_string_translators( + BerElement *ber, + BERTranslateProc encode_proc, + BERTranslateProc decode_proc +) +{ + ber->ber_encode_translate_proc = encode_proc; + ber->ber_decode_translate_proc = decode_proc; +} +#endif /* STR_TRANSLATION */ diff --git a/ldap/c-sdk/libraries/liblber/dtest.c b/ldap/c-sdk/libraries/liblber/dtest.c new file mode 100644 index 0000000000..71f13d45a8 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/dtest.c @@ -0,0 +1,113 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* dtest.c - lber decoding test program */ + +#include +#include +#ifdef MACOS +#include +#include +#else /* MACOS */ +#ifdef _WIN32 +#include +#else +#include +#include +#endif /* _WIN32 */ +#endif /* MACOS */ +#include "lber.h" + +int +SSL_Recv( int s, char *b, unsigned l, int dummy ) +{ + return( read( s, b, l ) ); +} + +SSL_Send( int s, char *b, unsigned l, int dummy ) +{ + return( write( s, b, l ) ); +} + +static void usage( char *name ) +{ + fprintf( stderr, "usage: %s < berfile\n", name ); +} + +main( int argc, char **argv ) +{ + long i, fd; + ber_len_t len; + ber_tag_t tag; + BerElement *ber; + Sockbuf *sb; + extern int lber_debug; + + lber_debug = 255; + if ( argc > 1 ) { + usage( argv[0] ); + exit( 1 ); + } + + sb = ber_sockbuf_alloc(); + fd = 0; + ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_DESC, &fd ); + + if ( (ber = der_alloc()) == NULL ) { + perror( "ber_alloc" ); + exit( 1 ); + } + + if ( (tag = ber_get_next( sb, &len, ber )) == LBER_ERROR ) { + perror( "ber_get_next" ); + exit( 1 ); + } + printf( "message has tag 0x%x and length %ld\n", tag, len ); + + return( 0 ); +} diff --git a/ldap/c-sdk/libraries/liblber/encode.c b/ldap/c-sdk/libraries/liblber/encode.c new file mode 100644 index 0000000000..1c47b12a02 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/encode.c @@ -0,0 +1,699 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* encode.c - ber output encoding routines */ + +#include "lber-int.h" + +static int +ber_calc_taglen( ber_tag_t tag ) +{ + int i; + ber_int_t mask; + + /* find the first non-all-zero byte in the tag */ + for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { + mask = (0xff << (i * 8)); + /* not all zero */ + if ( tag & mask ) + break; + } + + return( i + 1 ); +} + +static int +ber_put_tag( BerElement *ber, ber_tag_t tag, int nosos ) +{ + int taglen; + ber_tag_t ntag; + + taglen = ber_calc_taglen( tag ); + + ntag = LBER_HTONL( tag ); + + return( ber_write( ber, ((char *) &ntag) + sizeof(ber_int_t) - taglen, + taglen, nosos ) ); +} + +static int +ber_calc_lenlen( ber_len_t len ) +{ + /* + * short len if it's less than 128 - one byte giving the len, + * with bit 8 0. + */ + + if ( len <= 0x7F ) + return( 1 ); + + /* + * long len otherwise - one byte with bit 8 set, giving the + * length of the length, followed by the length itself. + */ + + if ( len <= 0xFF ) + return( 2 ); + if ( len <= 0xFFFF ) + return( 3 ); + if ( len <= 0xFFFFFF ) + return( 4 ); + + return( 5 ); +} + +static int +ber_put_len( BerElement *ber, ber_len_t len, int nosos ) +{ + int i; + char lenlen; + ber_int_t mask; + ber_len_t netlen; + + /* + * short len if it's less than 128 - one byte giving the len, + * with bit 8 0. + */ + + if ( len <= 127 ) { + netlen = LBER_HTONL( len ); + return( ber_write( ber, (char *) &netlen + sizeof(ber_int_t) - 1, + 1, nosos ) ); + } + + /* + * long len otherwise - one byte with bit 8 set, giving the + * length of the length, followed by the length itself. + */ + + /* find the first non-all-zero byte */ + for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { + mask = (0xff << (i * 8)); + /* not all zero */ + if ( len & mask ) + break; + } + lenlen = ++i; + if ( lenlen > 4 ) + return( -1 ); + lenlen |= 0x80; + + /* write the length of the length */ + if ( ber_write( ber, &lenlen, 1, nosos ) != 1 ) + return( -1 ); + + /* write the length itself */ + netlen = LBER_HTONL( len ); + if ( ber_write( ber, (char *) &netlen + (sizeof(ber_int_t) - i), i, nosos ) + != i ) + return( -1 ); + + return( i + 1 ); +} + +static int +ber_put_int_or_enum( BerElement *ber, ber_int_t num, ber_tag_t tag ) +{ + int i, sign, taglen; + int len, lenlen; + ber_int_t netnum, mask; + + sign = (num < 0); + + /* + * high bit is set - look for first non-all-one byte + * high bit is clear - look for first non-all-zero byte + */ + for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { + mask = (0xff << (i * 8)); + + if ( sign ) { + /* not all ones */ + if ( (num & mask) != mask ) + break; + } else { + /* not all zero */ + if ( num & mask ) + break; + } + } + + /* + * we now have the "leading byte". if the high bit on this + * byte matches the sign bit, we need to "back up" a byte. + */ + mask = (num & (0x80 << (i * 8))); + if ( (mask && !sign) || (sign && !mask) ) + i++; + + len = i + 1; + + if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) + return( -1 ); + + if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ) + return( -1 ); + i++; + netnum = LBER_HTONL( num ); + if ( ber_write( ber, (char *) &netnum + (sizeof(ber_int_t) - i), i, 0 ) + == i) + /* length of tag + length + contents */ + return( taglen + lenlen + i ); + + return( -1 ); +} + +int +LDAP_CALL +ber_put_enum( BerElement *ber, ber_int_t num, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) + tag = LBER_ENUMERATED; + + return( ber_put_int_or_enum( ber, num, tag ) ); +} + +int +LDAP_CALL +ber_put_int( BerElement *ber, ber_int_t num, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) + tag = LBER_INTEGER; + + return( ber_put_int_or_enum( ber, num, tag ) ); +} + +int +LDAP_CALL +ber_put_ostring( BerElement *ber, char *str, ber_len_t len, + ber_tag_t tag ) +{ + int taglen, lenlen, rc; +#ifdef STR_TRANSLATION + int free_str; +#endif /* STR_TRANSLATION */ + + if ( tag == LBER_DEFAULT ) + tag = LBER_OCTETSTRING; + + if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) + return( -1 ); + +#ifdef STR_TRANSLATION + if ( len > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) != 0 + && ber->ber_encode_translate_proc != NULL ) { + if ( (*(ber->ber_encode_translate_proc))( &str, &len, 0 ) + != 0 ) { + return( -1 ); + } + free_str = 1; + } else { + free_str = 0; + } +#endif /* STR_TRANSLATION */ + + /* + * Note: below is a spot where we limit ber_write + * to signed long (instead of unsigned long) + */ + + if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 || + ber_write( ber, str, len, 0 ) != (ber_int_t) len ) { + rc = -1; + } else { + /* return length of tag + length + contents */ + rc = taglen + lenlen + len; + } + +#ifdef STR_TRANSLATION + if ( free_str ) { + NSLBERI_FREE( str ); + } +#endif /* STR_TRANSLATION */ + + return( rc ); +} + +int +LDAP_CALL +ber_put_string( BerElement *ber, char *str, ber_tag_t tag ) +{ + return( ber_put_ostring( ber, str, (ber_len_t) strlen( str ), tag )); +} + +int +LDAP_CALL +ber_put_bitstring( BerElement *ber, char *str, + ber_len_t blen /* in bits */, ber_tag_t tag ) +{ + int taglen, lenlen, len; + unsigned char unusedbits; + + if ( tag == LBER_DEFAULT ) + tag = LBER_BITSTRING; + + if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) + return( -1 ); + + len = ( blen + 7 ) / 8; + unusedbits = (unsigned char) (len * 8 - blen); + if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 ) + return( -1 ); + + if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 ) + return( -1 ); + + if ( ber_write( ber, str, len, 0 ) != len ) + return( -1 ); + + /* return length of tag + length + unused bit count + contents */ + return( taglen + 1 + lenlen + len ); +} + +int +LDAP_CALL +ber_put_null( BerElement *ber, ber_tag_t tag ) +{ + int taglen; + + if ( tag == LBER_DEFAULT ) + tag = LBER_NULL; + + if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) + return( -1 ); + + if ( ber_put_len( ber, 0, 0 ) != 1 ) + return( -1 ); + + return( taglen + 1 ); +} + +int +LDAP_CALL +ber_put_boolean( BerElement *ber, ber_int_t boolval, ber_tag_t tag ) +{ + int taglen; + unsigned char trueval = 0xff; + unsigned char falseval = 0x00; + + if ( tag == LBER_DEFAULT ) + tag = LBER_BOOLEAN; + + if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) + return( -1 ); + + if ( ber_put_len( ber, 1, 0 ) != 1 ) + return( -1 ); + + if ( ber_write( ber, (char *)(boolval ? &trueval : &falseval), 1, 0 ) + != 1 ) + return( -1 ); + + return( taglen + 2 ); +} + +#define FOUR_BYTE_LEN 5 + + +/* the idea here is roughly this: we maintain a stack of these Seqorset + * structures. This is pushed when we see the beginning of a new set or + * sequence. It is popped when we see the end of a set or sequence. + * Since we don't want to malloc and free these structures all the time, + * we pre-allocate a small set of them within the ber element structure. + * thus we need to spot when we've overflowed this stack and fall back to + * malloc'ing instead. + */ +static int +ber_start_seqorset( BerElement *ber, ber_tag_t tag ) +{ + Seqorset *new_sos; + + /* can we fit into the local stack ? */ + if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) { + /* yes */ + new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn]; + } else { + /* no */ + if ( (new_sos = (Seqorset *)NSLBERI_MALLOC( sizeof(Seqorset))) + == NULLSEQORSET ) { + return( -1 ); + } + } + ber->ber_sos_stack_posn++; + + if ( ber->ber_sos == NULLSEQORSET ) + new_sos->sos_first = ber->ber_ptr; + else + new_sos->sos_first = ber->ber_sos->sos_ptr; + + /* Set aside room for a 4 byte length field */ + new_sos->sos_ptr = new_sos->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN; + new_sos->sos_tag = tag; + + new_sos->sos_next = ber->ber_sos; + new_sos->sos_clen = 0; + + ber->ber_sos = new_sos; + if (ber->ber_sos->sos_ptr > ber->ber_end) { + nslberi_ber_realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end); + } + return( 0 ); +} + +int +LDAP_CALL +ber_start_seq( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) + tag = LBER_SEQUENCE; + + return( ber_start_seqorset( ber, tag ) ); +} + +int +LDAP_CALL +ber_start_set( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) + tag = LBER_SET; + + return( ber_start_seqorset( ber, tag ) ); +} + +static int +ber_put_seqorset( BerElement *ber ) +{ + ber_len_t len, netlen; + int taglen, lenlen; + unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1; + Seqorset *next; + Seqorset **sos = &ber->ber_sos; + + if ( *sos == NULL ) { + /* No sequence or set to put... fatal error. */ + return( -1 ); + } + + /* + * If this is the toplevel sequence or set, we need to actually + * write the stuff out. Otherwise, it's already been put in + * the appropriate buffer and will be written when the toplevel + * one is written. In this case all we need to do is update the + * length and tag. + */ + + len = (*sos)->sos_clen; + netlen = LBER_HTONL( len ); + if ( sizeof(ber_int_t) > 4 && len > 0xFFFFFFFF ) + return( -1 ); + + if ( ber->ber_options & LBER_OPT_USE_DER ) { + lenlen = ber_calc_lenlen( len ); + } else { + lenlen = FOUR_BYTE_LEN; + } + + if ( (next = (*sos)->sos_next) == NULLSEQORSET ) { + /* write the tag */ + if ( (taglen = ber_put_tag( ber, (*sos)->sos_tag, 1 )) == -1 ) + return( -1 ); + + if ( ber->ber_options & LBER_OPT_USE_DER ) { + /* Write the length in the minimum # of octets */ + if ( ber_put_len( ber, len, 1 ) == -1 ) + return( -1 ); + + if (lenlen != FOUR_BYTE_LEN) { + /* + * We set aside FOUR_BYTE_LEN bytes for + * the length field. Move the data if + * we don't actually need that much + */ + SAFEMEMCPY( (*sos)->sos_first + taglen + + lenlen, (*sos)->sos_first + taglen + + FOUR_BYTE_LEN, len ); + } + } else { + /* Fill FOUR_BYTE_LEN bytes for length field */ + /* one byte of length length */ + if ( ber_write( ber, (char *)<ag, 1, 1 ) != 1 ) + return( -1 ); + + /* the length itself */ + if ( ber_write( ber, (char *) &netlen + sizeof(ber_int_t) + - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1 ) + != FOUR_BYTE_LEN - 1 ) + return( -1 ); + } + /* The ber_ptr is at the set/seq start - move it to the end */ + ber->ber_ptr += len; + } else { + ber_tag_t ntag; + + /* the tag */ + taglen = ber_calc_taglen( (*sos)->sos_tag ); + ntag = LBER_HTONL( (*sos)->sos_tag ); + SAFEMEMCPY( (*sos)->sos_first, (char *) &ntag + + sizeof(ber_int_t) - taglen, taglen ); + + if ( ber->ber_options & LBER_OPT_USE_DER ) { + ltag = (lenlen == 1) ? (unsigned char)len : + (unsigned char) (0x80 + (lenlen - 1)); + } + + /* one byte of length length */ + SAFEMEMCPY( (*sos)->sos_first + 1, <ag, 1 ); + + if ( ber->ber_options & LBER_OPT_USE_DER ) { + if (lenlen > 1) { + /* Write the length itself */ + SAFEMEMCPY( (*sos)->sos_first + 2, + (char *)&netlen + sizeof(ber_uint_t) - + (lenlen - 1), + lenlen - 1 ); + } + if (lenlen != FOUR_BYTE_LEN) { + /* + * We set aside FOUR_BYTE_LEN bytes for + * the length field. Move the data if + * we don't actually need that much + */ + SAFEMEMCPY( (*sos)->sos_first + taglen + + lenlen, (*sos)->sos_first + taglen + + FOUR_BYTE_LEN, len ); + } + } else { + /* the length itself */ + SAFEMEMCPY( (*sos)->sos_first + taglen + 1, + (char *) &netlen + sizeof(ber_int_t) - + (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1 ); + } + + next->sos_clen += (taglen + lenlen + len); + next->sos_ptr += (taglen + lenlen + len); + } + + /* we're done with this seqorset, so free it up */ + /* was this one from the local stack ? */ + if (ber->ber_sos_stack_posn <= SOS_STACK_SIZE) { + /* yes */ + } else { + /* no */ + NSLBERI_FREE( (char *) (*sos) ); + } + ber->ber_sos_stack_posn--; + *sos = next; + + return( taglen + lenlen + len ); +} + +int +LDAP_CALL +ber_put_seq( BerElement *ber ) +{ + return( ber_put_seqorset( ber ) ); +} + +int +LDAP_CALL +ber_put_set( BerElement *ber ) +{ + return( ber_put_seqorset( ber ) ); +} + +/* VARARGS */ +int +LDAP_C +ber_printf( BerElement *ber, const char *fmt, ... ) +{ + va_list ap; + char *s, **ss; + struct berval *bval, **bv; + int rc, i; + ber_len_t len; + + va_start( ap, fmt ); + +#ifdef LDAP_DEBUG + if ( lber_debug & 64 ) { + char msg[80]; + sprintf( msg, "ber_printf fmt (%s)\n", fmt ); + ber_err_print( msg ); + } +#endif + + for ( rc = 0; *fmt && rc != -1; fmt++ ) { + switch ( *fmt ) { + case 'b': /* boolean */ + i = va_arg( ap, int ); + rc = ber_put_boolean( ber, i, ber->ber_tag ); + break; + + case 'i': /* int */ + i = va_arg( ap, int ); + rc = ber_put_int( ber, (ber_int_t)i, ber->ber_tag ); + break; + + case 'e': /* enumeration */ + i = va_arg( ap, int ); + rc = ber_put_enum( ber, (ber_int_t)i, ber->ber_tag ); + break; + + case 'n': /* null */ + rc = ber_put_null( ber, ber->ber_tag ); + break; + + case 'o': /* octet string (non-null terminated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, int ); + rc = ber_put_ostring( ber, s, len, ber->ber_tag ); + break; + + case 'O': /* berval octet string */ + if( ( bval = va_arg( ap, struct berval * ) ) == NULL ) + break; + if( bval->bv_len == 0 ) { + rc = ber_put_ostring( ber, "", 0, ber->ber_tag ); + } else { + rc = ber_put_ostring( ber, bval->bv_val, bval->bv_len, + ber->ber_tag ); + } + break; + + case 's': /* string */ + s = va_arg( ap, char * ); + rc = ber_put_string( ber, s, ber->ber_tag ); + break; + + case 'B': /* bit string */ + s = va_arg( ap, char * ); + len = va_arg( ap, int ); /* in bits */ + rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); + break; + + case 't': /* tag for the next element */ + ber->ber_tag = va_arg( ap, ber_tag_t ); + ber->ber_usertag = 1; + break; + + case 'v': /* vector of strings */ + if ( (ss = va_arg( ap, char ** )) == NULL ) + break; + for ( i = 0; ss[i] != NULL; i++ ) { + if ( (rc = ber_put_string( ber, ss[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'V': /* sequences of strings + lengths */ + if ( (bv = va_arg( ap, struct berval ** )) == NULL ) + break; + for ( i = 0; bv[i] != NULL; i++ ) { + if ( (rc = ber_put_ostring( ber, bv[i]->bv_val, + bv[i]->bv_len, ber->ber_tag )) == -1 ) + break; + } + break; + + case '{': /* begin sequence */ + rc = ber_start_seq( ber, ber->ber_tag ); + break; + + case '}': /* end sequence */ + rc = ber_put_seqorset( ber ); + break; + + case '[': /* begin set */ + rc = ber_start_set( ber, ber->ber_tag ); + break; + + case ']': /* end set */ + rc = ber_put_seqorset( ber ); + break; + + default: { + char msg[80]; + sprintf( msg, "unknown fmt %c\n", *fmt ); + ber_err_print( msg ); + rc = -1; + break; + } + } + + if ( ber->ber_usertag == 0 ) + ber->ber_tag = LBER_DEFAULT; + else + ber->ber_usertag = 0; + } + + va_end( ap ); + + return( rc ); +} diff --git a/ldap/c-sdk/libraries/liblber/etest.c b/ldap/c-sdk/libraries/liblber/etest.c new file mode 100644 index 0000000000..f2bab358fc --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/etest.c @@ -0,0 +1,193 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* test.c - lber encoding test program */ + +#include +#include +#ifdef MACOS +#include +#include +#include +#include +#else /* MACOS */ +#include +#ifdef _WIN32 +#include +#else +#include +#endif /* _WIN32 */ +#endif /* MACOS */ +#include "lber.h" + +int +SSL_Recv( int s, char *b, unsigned l, int dummy ) +{ + return( read( s, b, l ) ); +} + +SSL_Send( int s, char *b, unsigned l, int dummy ) +{ + return( write( s, b, l ) ); +} + +int +getline( char *prompt, char c, char *buf, int bsize ) +{ + char *p; + + if ( prompt != NULL ) { + fprintf( stderr, "%s: ", prompt ); + } else { + fprintf( stderr, "enter value for '%c': ", c ); + } + if ( fgets( buf, bsize, stdin ) == NULL ) { + return( -1 ); + } + if ( (p = strchr( buf, '\n' )) != NULL ) { + *p = '\0'; + } + + return( 0 ); +} + + +static void usage( char *name ) +{ + fprintf( stderr, "usage: %s fmtstring\n", name ); +} + +main( int argc, char **argv ) +{ + int rc, fd; + char *s, *p; + void *arg1, *arg2; + Sockbuf *sb; + BerElement *ber; + char fmt[2]; + char buf[BUFSIZ]; + extern int lber_debug; + + lber_debug = 255; + if ( argc < 2 ) { + usage( argv[0] ); + exit( 1 ); + } + + sb = ber_sockbuf_alloc(); + fd = 1; + ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_DESC, &fd ); + + if ( (ber = der_alloc()) == NULL ) { + perror( "ber_alloc" ); + exit( 1 ); + } + + rc = 0; + fmt[1] = '\0'; + for ( s = argv[1]; *s; s++ ) { + switch ( *s ) { + case 'i': /* int */ + case 'b': /* boolean */ + case 'e': /* enumeration */ + getline( NULL, *s, buf, sizeof(buf) ); + arg1 = (void *) atoi( buf ); + break; + + case 'n': /* null */ + arg1 = NULL; + break; + + case 'o': /* octet string (non-null terminated) */ + getline( NULL, *s, buf, sizeof(buf) ); + arg1 = (void *) buf; + arg2 = (void *) strlen( buf ); + break; + + case 's': /* string */ + getline( NULL, *s, buf, sizeof(buf) ); + arg1 = (void *) buf; + break; + + case 'B': /* bit string */ + getline( NULL, *s, buf, sizeof(buf) ); + arg1 = (void *) buf; + arg2 = (void *) strlen( buf ); + break; + + case 't': /* tag for the next element */ + getline( NULL, *s, buf, sizeof(buf) ); + arg1 = (void *) buf; + break; + + case '{': /* begin sequence */ + case '}': /* end sequence */ + case '[': /* begin set */ + case ']': /* end set */ + break; + + default: + fprintf( stderr, "unknown fmt %c\n", *s ); + rc = -1; + break; + } + + fmt[0] = *s; + if ( ber_printf( ber, fmt, arg1, arg2 ) == -1 ) { + fprintf( stderr, "ber_printf\n" ); + exit( 1 ); + } + } + + if ( ber_flush( sb, ber, 1 ) != 0 ) { + perror( "ber_flush" ); + rc = -1; + } + + return( rc ); +} diff --git a/ldap/c-sdk/libraries/liblber/idtest.c b/ldap/c-sdk/libraries/liblber/idtest.c new file mode 100644 index 0000000000..e6f8fa4ae7 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/idtest.c @@ -0,0 +1,100 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* idtest.c - ber decoding test program using isode libraries */ + +#include +#include +#include + +static usage( char *name ) +{ + fprintf( stderr, "usage: %s\n", name ); +} + +main( int argc, char **argv ) +{ + PE pe; + PS psin, psout, pserr; + + /* read the pe from standard in */ + if ( (psin = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( 1 ); + } + if ( std_setup( psin, stdin ) == NOTOK ) { + perror( "std_setup" ); + exit( 1 ); + } + /* write the pe to standard out */ + if ( (psout = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( 1 ); + } + if ( std_setup( psout, stdout ) == NOTOK ) { + perror( "std_setup" ); + exit( 1 ); + } + /* pretty print it to standard error */ + if ( (pserr = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( 1 ); + } + if ( std_setup( pserr, stderr ) == NOTOK ) { + perror( "std_setup" ); + exit( 1 ); + } + + while ( (pe = ps2pe( psin )) != NULLPE ) { + pe2pl( pserr, pe ); + pe2ps( psout, pe ); + } + + exit( 0 ); +} diff --git a/ldap/c-sdk/libraries/liblber/io.c b/ldap/c-sdk/libraries/liblber/io.c new file mode 100644 index 0000000000..fc81a9deeb --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/io.c @@ -0,0 +1,1757 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* io.c - ber general i/o routines */ + +#include "lber-int.h" + +#define bergetc( sb, len ) ( sb->sb_ber.ber_end > sb->sb_ber.ber_ptr ? \ + (unsigned char)*sb->sb_ber.ber_ptr++ : \ + ber_filbuf( sb, len )) + +# ifdef macintosh +/* + * MacTCP/OpenTransport + */ +# define read( s, b, l ) tcpread( s, 0, (unsigned char *)b, l, NULL ) +# define MAX_WRITE 65535 +# define BerWrite( sb, b, l ) tcpwrite( sb->sb_sd, (unsigned char *)(b), (lsb_sd, b, l, 0 ) +# else /* _WIN32 */ +/* + * everything else (Unix/BSD 4.3 socket API) + */ +# define BerWrite( sb, b, l ) write( sb->sb_sd, b, l ) +# define udp_read( sb, b, l, al ) recvfrom(sb->sb_sd, (char *)b, l, 0, \ + (struct sockaddr *)sb->sb_fromaddr, \ + (al = sizeof(struct sockaddr), &al)) +# define udp_write( sb, b, l ) sendto(sb->sb_sd, (char *)(b), l, 0, \ + (struct sockaddr *)sb->sb_useaddr, sizeof(struct sockaddr)) +# endif /* _WIN32 */ +# endif /* macintosh */ + +#ifndef udp_read +#define udp_read( sb, b, l, al ) CLDAP NOT SUPPORTED +#define udp_write( sb, b, l ) CLDAP NOT SUPPORTED +#endif /* udp_read */ + +#define EXBUFSIZ 1024 +size_t lber_bufsize = EXBUFSIZ; + +#ifdef LDAP_DEBUG +int lber_debug; +#endif + +/* + * function prototypes + */ +static void nslberi_install_compat_io_fns( Sockbuf *sb ); +static int nslberi_extread_compat( int s, void *buf, int len, + struct lextiof_socket_private *arg ); +static int nslberi_extwrite_compat( int s, const void *buf, int len, + struct lextiof_socket_private *arg ); +static ber_tag_t get_tag( Sockbuf *sb, BerElement *ber); +static ber_len_t get_ber_len( BerElement *ber); +static ber_len_t read_len_in_ber( Sockbuf *sb, BerElement *ber); + +/* + * internal global structure for memory allocation callback functions + */ +static struct lber_memalloc_fns nslberi_memalloc_fns; + + +/* + * buffered read from "sb". + * returns value of first character read on success and -1 on error. + */ +static int +ber_filbuf( Sockbuf *sb, ber_slen_t len ) +{ + ssize_t rc; +#ifdef CLDAP + int addrlen; +#endif /* CLDAP */ + + if ( sb->sb_ber.ber_buf == NULL ) { + if ( (sb->sb_ber.ber_buf = (char *)NSLBERI_MALLOC( + READBUFSIZ )) == NULL ) { + return( -1 ); + } + sb->sb_ber.ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER; + sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf; + sb->sb_ber.ber_end = sb->sb_ber.ber_buf; + } + + if ( sb->sb_naddr > 0 ) { +#ifdef CLDAP + rc = udp_read(sb, sb->sb_ber.ber_buf, READBUFSIZ, addrlen ); +#ifdef LDAP_DEBUG + if ( lber_debug ) { + char msg[80]; + sprintf( msg, "ber_filbuf udp_read %d bytes\n", + rc ); + ber_err_print( msg ); + if ( lber_debug > 1 && rc > 0 ) + lber_bprint( sb->sb_ber.ber_buf, rc ); + } +#endif /* LDAP_DEBUG */ +#else /* CLDAP */ + rc = -1; +#endif /* CLDAP */ + } else { + if ( sb->sb_ext_io_fns.lbextiofn_read != NULL ) { + rc = sb->sb_ext_io_fns.lbextiofn_read( + sb->sb_sd, sb->sb_ber.ber_buf, + ((sb->sb_options & LBER_SOCKBUF_OPT_NO_READ_AHEAD) + && (len < READBUFSIZ)) ? len : READBUFSIZ, + sb->sb_ext_io_fns.lbextiofn_socket_arg ); + } else { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else + rc = read( sb->sb_sd, sb->sb_ber.ber_buf, + ((sb->sb_options & LBER_SOCKBUF_OPT_NO_READ_AHEAD) + && (len < READBUFSIZ)) ? len : READBUFSIZ ); +#endif + } + } + + if ( rc > 0 ) { + sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf + 1; + sb->sb_ber.ber_end = sb->sb_ber.ber_buf + rc; + return( (unsigned char)*sb->sb_ber.ber_buf ); + } + + return( -1 ); +} + + +static ber_int_t +BerRead( Sockbuf *sb, char *buf, ber_slen_t len ) +{ + int c; + ber_int_t nread = 0; + + while (len > 0) + { + ber_int_t inberbuf = sb->sb_ber.ber_end - sb->sb_ber.ber_ptr; + if (inberbuf > 0) + { + size_t tocopy = len > inberbuf ? inberbuf : len; + SAFEMEMCPY(buf, sb->sb_ber.ber_ptr, tocopy); + buf += tocopy; + sb->sb_ber.ber_ptr += tocopy; + nread += tocopy; + len -= tocopy; + } + else + { + c = ber_filbuf(sb, len); + if (c < 0) + { + if (nread > 0) + break; + else + return c; + } + *buf++ = c; + nread++; + len--; + } + } + return (nread); +} + + +/* + * Note: ber_read() only uses the ber_end and ber_ptr elements of ber. + * Functions like ber_get_tag(), ber_skip_tag, and ber_peek_tag() rely on + * that fact, so if this code is changed to use any additional elements of + * the ber structure, those functions will need to be changed as well. + */ +ber_int_t +LDAP_CALL +ber_read( BerElement *ber, char *buf, ber_len_t len ) +{ + ber_len_t actuallen; + ber_uint_t nleft; + + nleft = ber->ber_end - ber->ber_ptr; + actuallen = nleft < len ? nleft : len; + + SAFEMEMCPY( buf, ber->ber_ptr, (size_t)actuallen ); + + ber->ber_ptr += actuallen; + + return( (ber_int_t)actuallen ); +} + +/* + * enlarge the ber buffer. + * return 0 on success, -1 on error. + */ +int +nslberi_ber_realloc( BerElement *ber, ber_len_t len ) +{ + ber_uint_t need, have, total; + size_t have_bytes; + Seqorset *s; + ber_int_t off; + char *oldbuf; + int freeoldbuf = 0; + + ber->ber_buf_reallocs++; + + have_bytes = ber->ber_end - ber->ber_buf; + have = have_bytes / lber_bufsize; + need = (len < lber_bufsize ? 1 : (len + (lber_bufsize - 1)) / lber_bufsize); + total = have * lber_bufsize + need * lber_bufsize * ber->ber_buf_reallocs; + + oldbuf = ber->ber_buf; + + if (ber->ber_buf == NULL) { + if ( (ber->ber_buf = (char *)NSLBERI_MALLOC( (size_t)total )) + == NULL ) { + return( -1 ); + } + ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER; + } else { + if ( !(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER) ) { + freeoldbuf = 1; + } + /* transition to malloc'd buffer */ + if ( (ber->ber_buf = (char *)NSLBERI_MALLOC( + (size_t)total )) == NULL ) { + return( -1 ); + } + ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER; + /* copy existing data into new malloc'd buffer */ + SAFEMEMCPY( ber->ber_buf, oldbuf, have_bytes ); + } + + ber->ber_end = ber->ber_buf + total; + + /* + * If the stinking thing was moved, we need to go through and + * reset all the sos and ber pointers. Offsets would've been + * a better idea... oh well. + */ + + if ( ber->ber_buf != oldbuf ) { + ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf); + + for ( s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next ) { + off = s->sos_first - oldbuf; + s->sos_first = ber->ber_buf + off; + + off = s->sos_ptr - oldbuf; + s->sos_ptr = ber->ber_buf + off; + } + + if ( freeoldbuf && oldbuf ) { + NSLBERI_FREE( oldbuf ); + } + } + + return( 0 ); +} + +/* + * returns "len" on success and -1 on failure. + */ +ber_int_t +LDAP_CALL +ber_write( BerElement *ber, char *buf, ber_len_t len, int nosos ) +{ + if ( nosos || ber->ber_sos == NULL ) { + if ( ber->ber_ptr + len > ber->ber_end ) { + if ( nslberi_ber_realloc( ber, len ) != 0 ) + return( -1 ); + } + SAFEMEMCPY( ber->ber_ptr, buf, (size_t)len ); + ber->ber_ptr += len; + return( len ); + } else { + if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) { + if ( nslberi_ber_realloc( ber, len ) != 0 ) + return( -1 ); + } + SAFEMEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len ); + ber->ber_sos->sos_ptr += len; + ber->ber_sos->sos_clen += len; + return( len ); + } +} + +void +LDAP_CALL +ber_free( BerElement *ber, int freebuf ) +{ + if ( ber != NULL ) { + if ( freebuf && + !(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) { + NSLBERI_FREE(ber->ber_buf); + } + NSLBERI_FREE( (char *) ber ); + } +} + +/* + * return >= 0 on success, -1 on failure. + */ +int +LDAP_CALL +ber_flush( Sockbuf *sb, BerElement *ber, int freeit ) +{ + ssize_t nwritten = 0, towrite, rc; + int i = 0; + + if (ber->ber_rwptr == NULL) { + ber->ber_rwptr = ber->ber_buf; + } else if (ber->ber_rwptr >= ber->ber_end) { + /* we will use the ber_rwptr to continue an exited flush, + so if rwptr is not within the buffer we return an error. */ + return( -1 ); + } + + /* writev section - for iDAR only!!! */ + if (sb->sb_ext_io_fns.lbextiofn_writev != NULL) { + + /* add the sizes of the different buffers to write with writev */ + for (towrite = 0, i = 0; i < BER_ARRAY_QUANTITY; ++i) { + /* don't add lengths of null buffers - writev will ignore them */ + if (ber->ber_struct[i].ldapiov_base) { + towrite += ber->ber_struct[i].ldapiov_len; + } + } + + rc = sb->sb_ext_io_fns.lbextiofn_writev(sb->sb_sd, ber->ber_struct, BER_ARRAY_QUANTITY, + sb->sb_ext_io_fns.lbextiofn_socket_arg); + + if ( freeit ) + ber_free( ber, 1 ); + + if (rc >= 0) { + /* return the number of bytes TO BE written */ + return (towrite - rc); + } else { + /* otherwise it's an error */ + return (rc); + } + } /* end writev section */ + + towrite = ber->ber_ptr - ber->ber_rwptr; + +#ifdef LDAP_DEBUG + if ( lber_debug ) { + char msg[80]; + sprintf( msg, "ber_flush: %d bytes to sd %ld%s\n", (int)towrite, + sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)" + : "" ); + ber_err_print( msg ); + if ( lber_debug > 1 ) + lber_bprint( ber->ber_rwptr, towrite ); + } +#endif +#if !defined(macintosh) && !defined(DOS) + if ( sb->sb_options & (LBER_SOCKBUF_OPT_TO_FILE | LBER_SOCKBUF_OPT_TO_FILE_ONLY) ) { + rc = write( sb->sb_copyfd, ber->ber_buf, towrite ); + if ( sb->sb_options & LBER_SOCKBUF_OPT_TO_FILE_ONLY ) { + return( (int)rc ); + } + } +#endif + + nwritten = 0; + do { + if (sb->sb_naddr > 0) { +#ifdef CLDAP + rc = udp_write( sb, ber->ber_buf + nwritten, + (size_t)towrite ); +#else /* CLDAP */ + rc = -1; +#endif /* CLDAP */ + if ( rc <= 0 ) + return( -1 ); + /* fake error if write was not atomic */ + if (rc < towrite) { +#if !defined( macintosh ) && !defined( DOS ) + errno = EMSGSIZE; /* For Win32, see portable.h */ +#endif + return( -1 ); + } + } else { + if ( sb->sb_ext_io_fns.lbextiofn_write != NULL ) { + if ( (rc = sb->sb_ext_io_fns.lbextiofn_write( + sb->sb_sd, ber->ber_rwptr, (size_t)towrite, + sb->sb_ext_io_fns.lbextiofn_socket_arg )) + <= 0 ) { + return( -1 ); + } + } else { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else + if ( (rc = BerWrite( sb, ber->ber_rwptr, + (size_t) towrite )) <= 0 ) { + return( -1 ); + } +#endif + } + } + towrite -= rc; + nwritten += rc; + ber->ber_rwptr += rc; + } while ( towrite > 0 ); + + if ( freeit ) + ber_free( ber, 1 ); + + return( 0 ); +} + + +/* we pre-allocate a buffer to save the extra malloc later */ +BerElement * +LDAP_CALL +ber_alloc_t( int options ) +{ + BerElement *ber; + + if ( (ber = (BerElement*)NSLBERI_CALLOC( 1, + sizeof(struct berelement) + lber_bufsize )) == NULL ) { + return( NULL ); + } + + /* + * for compatibility with the C LDAP API standard, we recognize + * LBER_USE_DER as LBER_OPT_USE_DER. See lber.h for a bit more info. + */ + if ( options & LBER_USE_DER ) { + options &= ~LBER_USE_DER; + options |= LBER_OPT_USE_DER; + } + + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = options; + ber->ber_buf = (char*)ber + sizeof(struct berelement); + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + lber_bufsize; + ber->ber_flags = LBER_FLAG_NO_FREE_BUFFER; + + return( ber ); +} + + +BerElement * +LDAP_CALL +ber_alloc() +{ + return( ber_alloc_t( 0 ) ); +} + +BerElement * +LDAP_CALL +der_alloc() +{ + return( ber_alloc_t( LBER_OPT_USE_DER ) ); +} + +BerElement * +LDAP_CALL +ber_dup( BerElement *ber ) +{ + BerElement *new; + + if ( (new = ber_alloc()) == NULL ) + return( NULL ); + + *new = *ber; + + return( new ); +} + + +void +LDAP_CALL +ber_init_w_nullchar( BerElement *ber, int options ) +{ + (void) memset( (char *)ber, '\0', sizeof(struct berelement) ); + ber->ber_tag = LBER_DEFAULT; + + /* + * For compatibility with the C LDAP API standard, we recognize + * LBER_USE_DER as LBER_OPT_USE_DER. See lber.h for a bit more info. + */ + if ( options & LBER_USE_DER ) { + options &= ~LBER_USE_DER; + options |= LBER_OPT_USE_DER; + } + + ber->ber_options = options; +} + +void +LDAP_CALL +ber_reset( BerElement *ber, int was_writing ) +{ + if ( was_writing ) { + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; + } else { + ber->ber_ptr = ber->ber_end; + } + + ber->ber_rwptr = NULL; + ber->ber_tag_len_read = 0; + + memset(ber->ber_struct, 0, BER_CONTENTS_STRUCT_SIZE); +} + +/* Returns the length of the ber buffer so far, + taking into account sequences/sets also. + CAUTION: Returns 0 on null buffers as well + as 0 on empty buffers! +*/ +size_t +LDAP_CALL +ber_get_buf_datalen( BerElement *ber ) +{ + size_t datalen; + + if ( ( ber == NULL) || ( ber->ber_buf == NULL) || ( ber->ber_ptr == NULL ) ) { + datalen = 0; + } else if (ber->ber_sos == NULLSEQORSET) { + /* there are no sequences or sets yet, + so just subtract ptr from the beginning of the ber buffer */ + datalen = ber->ber_ptr - ber->ber_buf; + } else { + /* sequences exist, so just take the ptr of the sequence + on top of the stack and subtract the beginning of the + buffer from it */ + datalen = ber->ber_sos->sos_ptr - ber->ber_buf; + } + + return datalen; +} + +/* + if buf is 0 then malloc a buffer of length size + returns > 0 on success, 0 otherwise +*/ +int +LDAP_CALL +ber_stack_init(BerElement *ber, int options, char * buf, + size_t size) +{ + if (NULL == ber) + return 0; + + memset(ber, 0, sizeof(*ber)); + + /* + * for compatibility with the C LDAP API standard, we recognize + * LBER_USE_DER as LBER_OPT_USE_DER. See lber.h for a bit more info. + */ + + if ( options & LBER_USE_DER ) { + options &= ~LBER_USE_DER; + options |= LBER_OPT_USE_DER; + } + + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = options; + + if ( ber->ber_buf && !(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) { + NSLBERI_FREE(ber->ber_buf); + } + + if (buf) { + ber->ber_buf = ber->ber_ptr = buf; + ber->ber_flags = LBER_FLAG_NO_FREE_BUFFER; + } else { + ber->ber_buf = ber->ber_ptr = (char *) NSLBERI_MALLOC(size); + } + + ber->ber_end = ber->ber_buf + size; + + return ber->ber_buf != 0; +} + +/* + * This call allows to release only the data part of + * the target Sockbuf. + * Other info of this Sockbuf are kept unchanged. + */ +void +LDAP_CALL +ber_sockbuf_free_data(Sockbuf *p) +{ + if ( p != NULL ) { + if ( p->sb_ber.ber_buf != NULL && + !(p->sb_ber.ber_flags & LBER_FLAG_NO_FREE_BUFFER) ) { + NSLBERI_FREE( p->sb_ber.ber_buf ); + p->sb_ber.ber_buf = NULL; + } + } +} + +/* simply returns ber_buf in the ber ... + explicitly for DS MMR only... +*/ +char * +LDAP_CALL +ber_get_buf_databegin (BerElement * ber) +{ + if (NULL != ber) { + return ber->ber_buf; + } else { + return NULL; + } +} + +#ifdef LDAP_DEBUG + +void +ber_dump( BerElement *ber, int inout ) +{ + char msg[128]; + sprintf( msg, "ber_dump: buf 0x%p, ptr 0x%p, rwptr 0x%p, end 0x%p\n", + ber->ber_buf, ber->ber_ptr, ber->ber_rwptr, ber->ber_end ); + ber_err_print( msg ); + if ( inout == 1 ) { + sprintf( msg, " current len %ld, contents:\n", + (long)(ber->ber_end - ber->ber_ptr) ); + ber_err_print( msg ); + lber_bprint( ber->ber_ptr, ber->ber_end - ber->ber_ptr ); + } else { + sprintf( msg, " current len %ld, contents:\n", + (long)(ber->ber_ptr - ber->ber_buf) ); + ber_err_print( msg ); + lber_bprint( ber->ber_buf, ber->ber_ptr - ber->ber_buf ); + } +} + +void +ber_sos_dump( Seqorset *sos ) +{ + char msg[80]; + ber_err_print ( "*** sos dump ***\n" ); + while ( sos != NULLSEQORSET ) { + sprintf( msg, "ber_sos_dump: clen %d first 0x%p ptr 0x%p\n", + sos->sos_clen, sos->sos_first, sos->sos_ptr ); + ber_err_print( msg ); + sprintf( msg, " current len %ld contents:\n", + (long)(sos->sos_ptr - sos->sos_first) ); + ber_err_print( msg ); + lber_bprint( sos->sos_first, sos->sos_ptr - sos->sos_first ); + + sos = sos->sos_next; + } + ber_err_print( "*** end dump ***\n" ); +} + +#endif + +/* return the tag - LBER_DEFAULT returned means trouble + * assumes the tag is only one byte! */ +static ber_tag_t +get_tag( Sockbuf *sb, BerElement *ber) +{ + unsigned char xbyte; + + if ( (BerRead( sb, (char *) &xbyte, 1 )) != 1 ) { + return( LBER_DEFAULT ); + } + + /* we only handle small (one byte) tags */ + if ( (xbyte & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK ) { + return( LBER_DEFAULT ); + } + + ber->ber_tag_contents[0] = xbyte; + ber->ber_struct[BER_STRUCT_TAG].ldapiov_len = 1; + return((ber_tag_t)xbyte); +} + + +/* Error checking? */ +/* Takes a ber and returns the actual length */ +static ber_len_t +get_ber_len( BerElement *ber) +{ + int noctets; + ber_len_t len = 0; + char xbyte; + + xbyte = ber->ber_len_contents[0]; + + /* long form */ + if (xbyte & 0x80) { + noctets = (int) (xbyte & 0x7f); + if (noctets >= MAX_LEN_SIZE) { + return(LBER_DEFAULT); + } + SAFEMEMCPY((char*) &len + sizeof(ber_len_t) - noctets, &ber->ber_len_contents[1], noctets); + len = LBER_NTOHL(len); + return(len); + } else { + return((ber_len_t)(xbyte)); + } +} + +/* LBER_DEFAULT means trouble + reads in the length, stores it in ber->ber_struct, and returns get_ber_len */ +static ber_len_t +read_len_in_ber( Sockbuf *sb, BerElement *ber) +{ + unsigned char xbyte; + int noctets; + int rc = 0, read_result = 0; + + /* + * Next, read the length. The first byte contains the length + * of the length. If bit 8 is set, the length is the long + * form, otherwise it's the short form. We don't allow a + * length that's greater than what we can hold in a ber_int_t + */ + if ( ber->ber_tag_len_read == 1) { + /* the length of the length hasn't been read yet */ + if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 ) { + return( LBER_DEFAULT ); + } + ber->ber_tag_len_read = 2; + ber->ber_len_contents[0] = xbyte; + } else { + rc = ber->ber_tag_len_read - 2; + xbyte = ber->ber_len_contents[0]; + } + + /* long form of the length value */ + if ( xbyte & 0x80 ) { + noctets = (xbyte & 0x7f); + if ( noctets >= MAX_LEN_SIZE ) + return( LBER_DEFAULT ); + while (rc < noctets) { + read_result = BerRead( sb, &(ber->ber_len_contents[1]) + rc, noctets - rc ); + if (read_result <= 0) { + ber->ber_tag_len_read = rc + 2; /* so we can continue later - include tag and lenlen */ + return( LBER_DEFAULT ); + } + rc += read_result; + } + ber->ber_tag_len_read = rc + 2; /* adds tag (1 byte) and lenlen (1 byte) */ + ber->ber_struct[BER_STRUCT_LEN].ldapiov_len = 1 + noctets; + } else { /* short form of the length value */ + ber->ber_struct[BER_STRUCT_LEN].ldapiov_len = 1; + } + return(get_ber_len(ber)); +} + + +ber_tag_t +LDAP_CALL +ber_get_next( Sockbuf *sb, ber_len_t *len, BerElement *ber ) +{ + ber_len_t newlen; + ber_len_t toread; + ber_int_t rc; + ber_len_t orig_taglen_read = 0; + char * orig_rwptr = ber->ber_rwptr ? ber->ber_rwptr : ber->ber_buf; + +#ifdef LDAP_DEBUG + if ( lber_debug ) + ber_err_print( "ber_get_next\n" ); +#endif + + /* + * When rwptr is NULL this signifies that the tag and length have not been + * read in their entirety yet. (if at all) + */ + if ( ber->ber_rwptr == NULL ) { + + /* first save the amount we previously read, so we know what to return in len. */ + orig_taglen_read = ber->ber_tag_len_read; + + /* read the tag - if tag_len_read is greater than 0, then it has already been read. */ + if (ber->ber_tag_len_read == 0) { + if ((ber->ber_tag = get_tag(sb, ber)) == LBER_DEFAULT ) { + *len = 0; + return( LBER_DEFAULT ); + } + + ber->ber_tag_contents[0] = (char)ber->ber_tag; /* we only handle 1 byte tags */ + ber->ber_tag_len_read = 1; + + /* check for validity */ + if((sb->sb_options & LBER_SOCKBUF_OPT_VALID_TAG) && + (ber->ber_tag != sb->sb_valid_tag)) { + *len = 1; /* we just read the tag so far */ + return( LBER_DEFAULT); + } + } + + /* read the length */ + if ((newlen = read_len_in_ber(sb, ber)) == LBER_DEFAULT ) { + *len = ber->ber_tag_len_read - orig_taglen_read; + return( LBER_DEFAULT ); + } + + /* + * Finally, malloc a buffer for the contents and read it in. + * It's this buffer that's passed to all the other ber decoding + * routines. + */ + +#if defined( DOS ) && !( defined( _WIN32 ) || defined(XP_OS2) ) + if ( newlen > 65535 ) { /* DOS can't allocate > 64K */ + return( LBER_DEFAULT ); + } +#endif /* DOS && !_WIN32 */ + + if ( ( sb->sb_options & LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE ) + && newlen > sb->sb_max_incoming ) { + return( LBER_DEFAULT ); + } + + /* check to see if we already have enough memory allocated */ + if ( ((ber_len_t) ber->ber_end - (ber_len_t) ber->ber_buf) < newlen) { + if ( ber->ber_buf && !(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) { + NSLBERI_FREE(ber->ber_buf); + } + if ( (ber->ber_buf = (char *)NSLBERI_CALLOC( 1,(size_t)newlen )) + == NULL ) { + return( LBER_DEFAULT ); + } + ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER; + orig_rwptr = ber->ber_buf; + } + + + ber->ber_len = newlen; + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + newlen; + ber->ber_rwptr = ber->ber_buf; + ber->ber_tag_len_read = 0; /* now that rwptr is set, this doesn't matter */ + } + + /* OK, we've malloc-ed the buffer; now read the rest of the expected length */ + toread = (ber_len_t)ber->ber_end - (ber_len_t)ber->ber_rwptr; + do { + if ( (rc = BerRead( sb, ber->ber_rwptr, (ber_int_t)toread )) <= 0 ) { + *len = (ber_len_t) ber->ber_rwptr - (ber_len_t) orig_rwptr; + return( LBER_DEFAULT ); + } + + toread -= rc; + ber->ber_rwptr += rc; + } while ( toread > 0 ); + +#ifdef LDAP_DEBUG + if ( lber_debug ) { + char msg[80]; + sprintf( msg, "ber_get_next: tag 0x%x len %d contents:\n", + ber->ber_tag, ber->ber_len ); + ber_err_print( msg ); + if ( lber_debug > 1 ) + ber_dump( ber, 1 ); + } +#endif + + *len = (ber_len_t) ber->ber_rwptr - (ber_len_t) orig_rwptr; + ber->ber_rwptr = NULL; + ber->ber_struct[BER_STRUCT_VAL].ldapiov_len = ber->ber_len; + return( ber->ber_tag ); +} + +Sockbuf * +LDAP_CALL +ber_sockbuf_alloc() +{ + return( (Sockbuf *)NSLBERI_CALLOC( 1, sizeof(struct sockbuf) ) ); +} + +void +LDAP_CALL +ber_sockbuf_free(Sockbuf *p) +{ + if ( p != NULL ) { + if ( p->sb_ber.ber_buf != NULL && + !(p->sb_ber.ber_flags & LBER_FLAG_NO_FREE_BUFFER) ) { + NSLBERI_FREE( p->sb_ber.ber_buf ); + } + NSLBERI_FREE(p); + } +} + +/* + * return 0 on success and -1 on error + */ +int +LDAP_CALL +ber_set_option( struct berelement *ber, int option, void *value ) +{ + + /* + * memory allocation callbacks are global, so it is OK to pass + * NULL for ber. Handle this as a special case. + */ + if ( option == LBER_OPT_MEMALLOC_FN_PTRS ) { + /* struct copy */ + nslberi_memalloc_fns = *((struct lber_memalloc_fns *)value); + return( 0 ); + } + + /* + * lber_debug is global, so it is OK to pass + * NULL for ber. Handle this as a special case. + */ + if ( option == LBER_OPT_DEBUG_LEVEL ) { +#ifdef LDAP_DEBUG + lber_debug = *(int *)value; +#endif + return( 0 ); + } + + /* + * lber_bufsize is global, so it is OK to pass + * NULL for ber. Handle this as a special case. + */ + if ( option == LBER_OPT_BUFSIZE ) { + if ( *(size_t *)value > EXBUFSIZ ) { + lber_bufsize = *(size_t *)value; + } + return( 0 ); + } + + /* + * all the rest require a non-NULL ber + */ + if ( !NSLBERI_VALID_BERELEMENT_POINTER( ber )) { + return( -1 ); + } + + switch ( option ) { + case LBER_OPT_USE_DER: + case LBER_OPT_TRANSLATE_STRINGS: + if ( value != NULL ) { + ber->ber_options |= option; + } else { + ber->ber_options &= ~option; + } + break; + case LBER_OPT_REMAINING_BYTES: + ber->ber_end = ber->ber_ptr + *((ber_len_t *)value); + break; + case LBER_OPT_TOTAL_BYTES: + ber->ber_end = ber->ber_buf + *((ber_len_t *)value); + break; + case LBER_OPT_BYTES_TO_WRITE: + ber->ber_ptr = ber->ber_buf + *((ber_len_t *)value); + break; + default: + return( -1 ); + } + + return( 0 ); +} + +/* + * return 0 on success and -1 on error + */ +int +LDAP_CALL +ber_get_option( struct berelement *ber, int option, void *value ) +{ + /* + * memory callocation callbacks are global, so it is OK to pass + * NULL for ber. Handle this as a special case + */ + if ( option == LBER_OPT_MEMALLOC_FN_PTRS ) { + /* struct copy */ + *((struct lber_memalloc_fns *)value) = nslberi_memalloc_fns; + return( 0 ); + } + + /* + * lber_debug is global, so it is OK to pass + * NULL for ber. Handle this as a special case. + */ + if ( option == LBER_OPT_DEBUG_LEVEL ) { +#ifdef LDAP_DEBUG + *(int *)value = lber_debug; +#endif + return( 0 ); + } + + /* + * lber_bufsize is global, so it is OK to pass + * NULL for ber. Handle this as a special case. + */ + if ( option == LBER_OPT_BUFSIZE ) { + *(size_t *)value = lber_bufsize; + return( 0 ); + } + + /* + * all the rest require a non-NULL ber + */ + if ( !NSLBERI_VALID_BERELEMENT_POINTER( ber )) { + return( -1 ); + } + + switch ( option ) { + case LBER_OPT_USE_DER: + case LBER_OPT_TRANSLATE_STRINGS: + *((int *) value) = (ber->ber_options & option); + break; + case LBER_OPT_REMAINING_BYTES: + *((ber_len_t *) value) = ber->ber_end - ber->ber_ptr; + break; + case LBER_OPT_TOTAL_BYTES: + *((ber_len_t *) value) = ber->ber_end - ber->ber_buf; + break; + case LBER_OPT_BYTES_TO_WRITE: + *((ber_len_t *) value) = ber->ber_ptr - ber->ber_buf; + break; + default: + return( -1 ); + } + + return( 0 ); +} + +/* + * return 0 on success and -1 on error + */ +int +LDAP_CALL +ber_sockbuf_set_option( Sockbuf *sb, int option, void *value ) +{ + struct lber_x_ext_io_fns *extiofns; + + if ( !NSLBERI_VALID_SOCKBUF_POINTER( sb )) { + return( -1 ); + } + + /* check for a NULL value for certain options. */ + if (NULL == value) { + switch ( option ) { + case LBER_SOCKBUF_OPT_TO_FILE: + case LBER_SOCKBUF_OPT_TO_FILE_ONLY: + case LBER_SOCKBUF_OPT_NO_READ_AHEAD: + case LBER_SOCKBUF_OPT_READ_FN: + case LBER_SOCKBUF_OPT_WRITE_FN: + case LBER_SOCKBUF_OPT_EXT_IO_FNS: + case LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE: + /* do nothing - it's OK to have a NULL value for these options */ + break; + default: + return( -1 ); + } + } + + switch ( option ) { + case LBER_SOCKBUF_OPT_VALID_TAG: + sb->sb_valid_tag= *((ber_tag_t *) value); + /* use NULL to reset */ + if ( value != NULL ) { + sb->sb_options |= option; + } else { + sb->sb_options &= ~option; + } + break; + case LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE: + if ( value != NULL ) { + sb->sb_max_incoming = *((ber_len_t *) value); + sb->sb_options |= option; + } else { + /* setting the max incoming to 0 seems to be the only + way to tell the callers of ber_sockbuf_get_option + that this option isn't set. */ + sb->sb_max_incoming = 0; + sb->sb_options &= ~option; + } + break; + case LBER_SOCKBUF_OPT_TO_FILE: + case LBER_SOCKBUF_OPT_TO_FILE_ONLY: + case LBER_SOCKBUF_OPT_NO_READ_AHEAD: + if ( value != NULL ) { + sb->sb_options |= option; + } else { + sb->sb_options &= ~option; + } + break; + case LBER_SOCKBUF_OPT_DESC: + sb->sb_sd = *((LBER_SOCKET *) value); + break; + case LBER_SOCKBUF_OPT_COPYDESC: + sb->sb_copyfd = *((LBER_SOCKET *) value); + break; + case LBER_SOCKBUF_OPT_READ_FN: + sb->sb_io_fns.lbiof_read = (LDAP_IOF_READ_CALLBACK *) value; + nslberi_install_compat_io_fns( sb ); + break; + case LBER_SOCKBUF_OPT_WRITE_FN: + sb->sb_io_fns.lbiof_write = (LDAP_IOF_WRITE_CALLBACK *) value; + nslberi_install_compat_io_fns( sb ); + break; + case LBER_SOCKBUF_OPT_EXT_IO_FNS: + extiofns = (struct lber_x_ext_io_fns *) value; + if ( extiofns == NULL ) { /* remove */ + (void)memset( (char *)&sb->sb_ext_io_fns, '\0', + sizeof(sb->sb_ext_io_fns )); + } else if ( extiofns->lbextiofn_size + == LBER_X_EXTIO_FNS_SIZE ) { + /* struct copy */ + sb->sb_ext_io_fns = *extiofns; + } else if ( extiofns->lbextiofn_size + == LBER_X_EXTIO_FNS_SIZE_REV0 ) { + /* backwards compatiblity for older struct */ + sb->sb_ext_io_fns.lbextiofn_size = + LBER_X_EXTIO_FNS_SIZE; + sb->sb_ext_io_fns.lbextiofn_read = + extiofns->lbextiofn_read; + sb->sb_ext_io_fns.lbextiofn_write = + extiofns->lbextiofn_write; + sb->sb_ext_io_fns.lbextiofn_writev = NULL; + sb->sb_ext_io_fns.lbextiofn_socket_arg = + extiofns->lbextiofn_socket_arg; + } else { + return( -1 ); + } + break; + case LBER_SOCKBUF_OPT_SOCK_ARG: + sb->sb_ext_io_fns.lbextiofn_socket_arg = + (struct lextiof_socket_private *) value; + break; + default: + return( -1 ); + } + + return( 0 ); +} + +/* + * return 0 on success and -1 on error + */ +int +LDAP_CALL +ber_sockbuf_get_option( Sockbuf *sb, int option, void *value ) +{ + struct lber_x_ext_io_fns *extiofns; + + if ( !NSLBERI_VALID_SOCKBUF_POINTER( sb ) || (NULL == value)) { + return( -1 ); + } + + switch ( option ) { + case LBER_SOCKBUF_OPT_VALID_TAG: + *((ber_tag_t *) value) = sb->sb_valid_tag; + break; + case LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE: + *((ber_len_t *) value) = sb->sb_max_incoming; + break; + case LBER_SOCKBUF_OPT_TO_FILE: + case LBER_SOCKBUF_OPT_TO_FILE_ONLY: + case LBER_SOCKBUF_OPT_NO_READ_AHEAD: + *((int *) value) = (sb->sb_options & option); + break; + case LBER_SOCKBUF_OPT_DESC: + *((LBER_SOCKET *) value) = sb->sb_sd; + break; + case LBER_SOCKBUF_OPT_COPYDESC: + *((LBER_SOCKET *) value) = sb->sb_copyfd; + break; + case LBER_SOCKBUF_OPT_READ_FN: + *((LDAP_IOF_READ_CALLBACK **) value) + = sb->sb_io_fns.lbiof_read; + break; + case LBER_SOCKBUF_OPT_WRITE_FN: + *((LDAP_IOF_WRITE_CALLBACK **) value) + = sb->sb_io_fns.lbiof_write; + break; + case LBER_SOCKBUF_OPT_EXT_IO_FNS: + extiofns = (struct lber_x_ext_io_fns *) value; + if ( extiofns == NULL ) { + return( -1 ); + } else if ( extiofns->lbextiofn_size + == LBER_X_EXTIO_FNS_SIZE ) { + /* struct copy */ + *extiofns = sb->sb_ext_io_fns; + } else if ( extiofns->lbextiofn_size + == LBER_X_EXTIO_FNS_SIZE_REV0 ) { + /* backwards compatiblity for older struct */ + extiofns->lbextiofn_read = sb->sb_ext_io_fns.lbextiofn_read; + extiofns->lbextiofn_write = sb->sb_ext_io_fns.lbextiofn_write; + extiofns->lbextiofn_socket_arg = sb->sb_ext_io_fns.lbextiofn_socket_arg; + } else { + return( -1 ); + } + break; + case LBER_SOCKBUF_OPT_SOCK_ARG: + *((struct lextiof_socket_private **)value) = sb->sb_ext_io_fns.lbextiofn_socket_arg; + break; + default: + return( -1 ); + } + + return( 0 ); +} + + +/* new dboreham code below: */ + +struct byte_buffer { + unsigned char *p; + int offset; + int length; +}; +typedef struct byte_buffer byte_buffer; + + +/* This call allocates us a BerElement structure plus some extra memory. + * It returns a pointer to the BerElement, plus a pointer to the extra memory. + * This routine also allocates a ber data buffer within the same block, thus + * saving a call to calloc later when we read data. + */ +void* +LDAP_CALL +ber_special_alloc(size_t size, BerElement **ppBer) +{ + char *mem = NULL; + + /* Make sure mem size requested is aligned */ + if (0 != ( size & 0x03 )) { + size += (sizeof(ber_int_t) - (size & 0x03)); + } + + mem = NSLBERI_MALLOC(sizeof(struct berelement) + lber_bufsize + size ); + if (NULL == mem) { + return NULL; + } + *ppBer = (BerElement*) (mem + size); + memset(*ppBer,0,sizeof(struct berelement)); + (*ppBer)->ber_tag = LBER_DEFAULT; + (*ppBer)->ber_buf = mem + size + sizeof(struct berelement); + (*ppBer)->ber_ptr = (*ppBer)->ber_buf; + (*ppBer)->ber_end = (*ppBer)->ber_buf + lber_bufsize; + (*ppBer)->ber_flags = LBER_FLAG_NO_FREE_BUFFER; + return (void*)mem; +} + +void +LDAP_CALL +ber_special_free(void* buf, BerElement *ber) +{ + if (!(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) { + NSLBERI_FREE(ber->ber_buf); + } + NSLBERI_FREE( buf ); +} + +/* Copy up to bytes_to_read bytes from b into return_buffer. + * Returns a count of bytes copied (always >= 0). + */ +static int +read_bytes(byte_buffer *b, unsigned char *return_buffer, int bytes_to_read) +{ + /* copy up to bytes_to_read bytes into the caller's buffer, return the number of bytes copied */ + int bytes_to_copy = 0; + + if (bytes_to_read <= (b->length - b->offset) ) { + bytes_to_copy = bytes_to_read; + } else { + bytes_to_copy = (b->length - b->offset); + } + if (1 == bytes_to_copy) { + *return_buffer = *(b->p+b->offset++); + } else if (bytes_to_copy <= 0) { + bytes_to_copy = 0; /* never return a negative result */ + } else { + SAFEMEMCPY(return_buffer,b->p+b->offset,bytes_to_copy); + b->offset += bytes_to_copy; + } + return bytes_to_copy; +} + +/* return the tag - LBER_DEFAULT returned means trouble */ +static ber_tag_t +get_buffer_tag(byte_buffer *sb ) +{ + unsigned char xbyte; + ber_tag_t tag; + char *tagp; + int i; + + if ( (i = read_bytes( sb, &xbyte, 1 )) != 1 ) { + return( LBER_DEFAULT ); + } + + if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) { + return( (ber_uint_t) xbyte ); + } + + tagp = (char *) &tag; + tagp[0] = xbyte; + for ( i = 1; i < sizeof(ber_int_t); i++ ) { + if ( read_bytes( sb, &xbyte, 1 ) != 1 ) + return( LBER_DEFAULT ); + + tagp[i] = xbyte; + + if ( ! (xbyte & LBER_MORE_TAG_MASK) ) + break; + } + + /* tag too big! */ + if ( i == sizeof(ber_int_t) ) + return( LBER_DEFAULT ); + + /* want leading, not trailing 0's */ + return( tag >> (sizeof(ber_int_t) - i - 1) ); +} + +/* Like ber_get_next, but from a byte buffer the caller already has. */ +/* Bytes_Scanned returns the number of bytes we actually looked at in the buffer. */ +/* ber_get_next_buffer is now implemented in terms of ber_get_next_buffer_ext */ +/* and is here for backward compatibility. This new function allows us to pass */ +/* the Sockbuf structure along */ + +ber_uint_t +LDAP_CALL +ber_get_next_buffer( void *buffer, size_t buffer_size, ber_len_t *len, + BerElement *ber, ber_uint_t *Bytes_Scanned ) +{ + return (ber_get_next_buffer_ext( buffer, buffer_size, len, ber, + Bytes_Scanned, NULL)); +} + +/* + * Returns the tag of the message or LBER_ return code if an error occurs. + * + * If there was not enough data in the buffer to complete the message this + * is a "soft" error. In this case, *Bytes_Scanned is set to a positive + * number and return code is set to LBER_DEFAULT. + * + * On overflow condition when the length is either bigger than ber_uint_t + * type or the value preset via LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE option, + * *Bytes_Scanned is set to zero and return code is set to LBER_OVERFLOW. + * + * For backward compatibility errno is also set on these error conditions: + * + * EINVAL - LBER_SOCKBUF_OPT_VALID_TAG option set but tag doesnt match. + * EMSGSIZE - an overflow condition as described above for LBER_OVERFLOW. + */ +ber_uint_t +LDAP_CALL +ber_get_next_buffer_ext( void *buffer, size_t buffer_size, ber_len_t *len, + BerElement *ber, ber_uint_t *Bytes_Scanned, Sockbuf *sock ) +{ + ber_tag_t tag = 0; + ber_len_t netlen; + ber_len_t toread; + unsigned char lc; + ssize_t rc; + int noctets, diff; + byte_buffer sb = {0}; + + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag byte (we + * can assume a single byte), return the length in len, + * and the rest of the undecoded element in buf. + * + * Assumptions: + * 1) small tags (less than 128) + * 2) definite lengths + * 3) primitive encodings used whenever possible + */ + + /* + * first time through - malloc the buffer, set up ptrs, and + * read the tag and the length and as much of the rest as we can + */ + + sb.p = buffer; + sb.length = buffer_size; + + if ( ber->ber_rwptr == NULL ) { + + /* + * First, we read the tag. + */ + + /* if we have been called before with a fragment not + * containing a complete length, we have no rwptr but + * a tag already + */ + if ( ber->ber_tag == LBER_DEFAULT ) { + if ( (tag = get_buffer_tag( &sb )) == LBER_DEFAULT ) { + goto premature_exit; + } + ber->ber_tag = tag; + } + + if((sock->sb_options & LBER_SOCKBUF_OPT_VALID_TAG) && + (ber->ber_tag != sock->sb_valid_tag)) { +#if !defined( macintosh ) && !defined( DOS ) + errno = EINVAL; +#endif + goto error_exit; + } + + /* If we have been called before with an incomplete length, + * the fragment of the length read is in ber->ber_len_contents + * ber->ber_tag_len_read is the # of bytes of the length available + * from a previous fragment + */ + + if (ber->ber_tag_len_read) { + int nbytes; + + noctets = ((ber->ber_len_contents[0]) & 0x7f); + diff = noctets + 1 /* tag */ - ber->ber_tag_len_read; + + if ( (nbytes = read_bytes( &sb, (unsigned char *) &ber->ber_len_contents[0] + + ber->ber_tag_len_read, diff )) != diff ) { + + if (nbytes > 0) + ber->ber_tag_len_read+=nbytes; + + goto premature_exit; + } + *len = get_ber_len(ber); /* cast ber->ber_len_contents to unsigned long */ + + } else { + /* + * Next, read the length. The first byte contains the length + * of the length. If bit 8 is set, the length is the long + * form, otherwise it's the short form. We don't allow a + * length that's greater than what we can hold in an unsigned + * long. + */ + + *len = netlen = 0; + if ( read_bytes( &sb, &lc, 1 ) != 1 ) { + goto premature_exit; + } + if ( lc & 0x80 ) { + int nbytes; + + noctets = (lc & 0x7f); + if ( noctets > sizeof(ber_uint_t) ) { +#if !defined( macintosh ) && !defined( DOS ) + errno = EMSGSIZE; +#endif + *Bytes_Scanned = 0; + return(LBER_OVERFLOW); + } + diff = sizeof(ber_uint_t) - noctets; + if ( (nbytes = read_bytes( &sb, (unsigned char *)&netlen + diff, + noctets )) != noctets ) { + /* + * The length is in long form and we don't get it in one + * fragment, so stash partial length in the ber element + * for later use + */ + + ber->ber_tag_len_read = nbytes + 1; + ber->ber_len_contents[0]=lc; + memset(&(ber->ber_len_contents[1]), 0, sizeof(ber_uint_t)); + SAFEMEMCPY(&(ber->ber_len_contents[1]), (unsigned char *)&netlen + diff, nbytes); + + goto premature_exit; + } + *len = LBER_NTOHL( netlen ); + } else { + *len = lc; + } + } + + ber->ber_len = *len; + /* length fully decoded */ + ber->ber_tag_len_read=0; + /* + * Finally, malloc a buffer for the contents and read it in. + * It's this buffer that's passed to all the other ber decoding + * routines. + */ + +#if defined( DOS ) && !defined( _WIN32 ) + if ( *len > 65535 ) { /* DOS can't allocate > 64K */ + goto premature_exit; + } +#endif /* DOS && !_WIN32 */ + + if ( (sock != NULL) && + ( sock->sb_options & LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE ) + && (*len > sock->sb_max_incoming) ) { +#if !defined( macintosh ) && !defined( DOS ) + errno = EMSGSIZE; +#endif + *Bytes_Scanned = 0; + return( LBER_OVERFLOW ); + } + + if ( ber->ber_buf + *len > ber->ber_end ) { + if ( nslberi_ber_realloc( ber, *len ) != 0 ) + goto error_exit; + } + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + *len; + ber->ber_rwptr = ber->ber_buf; + } + + toread = (ber_len_t)ber->ber_end - (ber_len_t)ber->ber_rwptr; + do { + if ( (rc = read_bytes( &sb, (unsigned char *)ber->ber_rwptr, + (ber_int_t)toread )) <= 0 ) { + goto premature_exit; + } + + toread -= rc; + ber->ber_rwptr += rc; + } while ( toread > 0 ); + + *len = ber->ber_len; + *Bytes_Scanned = sb.offset; + return( ber->ber_tag ); + +premature_exit: + /* + * we're here because we hit the end of the buffer before seeing + * all of the PDU + */ + *Bytes_Scanned = sb.offset; + return(LBER_DEFAULT); + +error_exit: + *Bytes_Scanned = 0; + return(LBER_DEFAULT); +} + + +/* The ber_flatten routine allocates a struct berval whose contents + * are a BER encoding taken from the ber argument. The bvPtr pointer + * points to the returned berval, which must be freed using + * ber_bvfree(). This routine returns 0 on success and -1 on error. + * The use of ber_flatten on a BerElement in which all '{' and '}' + * format modifiers have not been properly matched can result in a + * berval whose contents are not a valid BER encoding. + * Note that the ber_ptr is not modified. + */ +int +LDAP_CALL +ber_flatten( BerElement *ber, struct berval **bvPtr ) +{ + struct berval *new; + ber_len_t len; + + /* allocate a struct berval */ + if ( (new = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) )) + == NULL ) { + return( -1 ); + } + + /* + * Copy everything from the BerElement's ber_buf to ber_ptr + * into the berval structure. + */ + if ( ber == NULL ) { + new->bv_val = NULL; + new->bv_len = 0; + } else { + len = ber->ber_ptr - ber->ber_buf; + if ( ( new->bv_val = (char *)NSLBERI_MALLOC( len + 1 )) == NULL ) { + ber_bvfree( new ); + return( -1 ); + } + SAFEMEMCPY( new->bv_val, ber->ber_buf, (size_t)len ); + new->bv_val[len] = '\0'; + new->bv_len = len; + } + + /* set bvPtr pointer to point to the returned berval */ + *bvPtr = new; + + return( 0 ); +} + + +/* + * The ber_init function constructs and returns a new BerElement + * containing a copy of the data in the bv argument. ber_init + * returns the null pointer on error. + */ +BerElement * +LDAP_CALL +ber_init( const struct berval *bv ) +{ + BerElement *ber; + + /* construct BerElement */ + if (( ber = ber_alloc_t( 0 )) != NULLBER ) { + /* copy data from the bv argument into BerElement */ + /* XXXmcs: had to cast unsigned long bv_len to long */ + if ( (ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) + != (ber_slen_t)bv->bv_len ) { + ber_free( ber, 1 ); + return( NULL ); + } + } + + /* + * reset ber_ptr back to the beginning of buffer so that this new + * and initialized ber element can be READ + */ + ber_reset( ber, 1); + + /* + * return a ptr to a new BerElement containing a copy of the data + * in the bv argument or a null pointer on error + */ + return( ber ); +} + + +/* + * memory allocation functions. + */ +void * +nslberi_malloc( size_t size ) +{ + return( nslberi_memalloc_fns.lbermem_malloc == NULL ? + malloc( size ) : + nslberi_memalloc_fns.lbermem_malloc( size )); +} + + +void * +nslberi_calloc( size_t nelem, size_t elsize ) +{ + return( nslberi_memalloc_fns.lbermem_calloc == NULL ? + calloc( nelem, elsize ) : + nslberi_memalloc_fns.lbermem_calloc( nelem, elsize )); +} + + +void * +nslberi_realloc( void *ptr, size_t size ) +{ + return( nslberi_memalloc_fns.lbermem_realloc == NULL ? + realloc( ptr, size ) : + nslberi_memalloc_fns.lbermem_realloc( ptr, size )); +} + + +void +nslberi_free( void *ptr ) +{ + if ( nslberi_memalloc_fns.lbermem_free == NULL ) { + free( ptr ); + } else { + nslberi_memalloc_fns.lbermem_free( ptr ); + } +} + + +/* + ****************************************************************************** + * functions to bridge the gap between new extended I/O functions that are + * installed using ber_sockbuf_set_option( ..., LBER_SOCKBUF_OPT_EXT_IO_FNS, + * ... ). + * + * the basic strategy is to use the new extended arg to hold a pointer to the + * Sockbuf itself so we can find the old functions and call them. + * note that the integer socket s passed in is not used. we use the sb_sd + * from the Sockbuf itself because it is the correct type. + */ +static int +nslberi_extread_compat( int s, void *buf, int len, + struct lextiof_socket_private *arg ) +{ + Sockbuf *sb = (Sockbuf *)arg; + + return( sb->sb_io_fns.lbiof_read( sb->sb_sd, buf, len )); +} + + +static int +nslberi_extwrite_compat( int s, const void *buf, int len, + struct lextiof_socket_private *arg ) +{ + Sockbuf *sb = (Sockbuf *)arg; + + return( sb->sb_io_fns.lbiof_write( sb->sb_sd, buf, len )); +} + + +/* + * Install I/O compatiblity functions. This can't fail. + */ +static void +nslberi_install_compat_io_fns( Sockbuf *sb ) +{ + sb->sb_ext_io_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + sb->sb_ext_io_fns.lbextiofn_read = nslberi_extread_compat; + sb->sb_ext_io_fns.lbextiofn_write = nslberi_extwrite_compat; + sb->sb_ext_io_fns.lbextiofn_writev = NULL; + sb->sb_ext_io_fns.lbextiofn_socket_arg = (void *)sb; +} +/* + * end of compat I/O functions + ****************************************************************************** + */ diff --git a/ldap/c-sdk/libraries/liblber/lber-int.h b/ldap/c-sdk/libraries/liblber/lber-int.h new file mode 100644 index 0000000000..24b15bdc5f --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/lber-int.h @@ -0,0 +1,305 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* lbet-int.h - internal header file for liblber */ + +#ifndef _LBERINT_H +#define _LBERINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#ifdef macintosh +# include "ldap-macos.h" +#else /* macintosh */ +#if !defined(BSDI) && !defined(DARWIN) && !defined(FREEBSD) && !defined(OPENBSD) +# include +#endif +# include +# include +#if defined(SUNOS4) || defined(SCOOS) +# include +#endif +#if defined( _WINDOWS ) +# define WIN32_LEAN_AND_MEAN +# include +# include +# define ssize_t SSIZE_T +# include +/* No stderr in a 16-bit Windows DLL */ +# if defined(_WINDLL) && !defined(_WIN32) +# define USE_DBG_WIN +# endif +# else +/* # include */ +# include +# include +#if !defined(XP_OS2) && !defined(DARWIN) +# include +#endif +# endif /* defined( _WINDOWS ) */ +#endif /* macintosh */ + +#include +#include +#include "portable.h" + +#ifdef _WINDOWS +# if defined(FD_SETSIZE) +# else +# define FD_SETSIZE 256 /* set it before winsock sets it to 64! */ +# endif +#include +#include +#endif /* _WINDOWS */ + +#ifdef XP_OS2 +#include +#endif /* XP_OS2 */ + +/* No stderr in a 16-bit Windows DLL */ +#if defined(_WINDLL) && !defined(_WIN32) +#define stderr NULL +#endif + +#include "lber.h" + +#ifdef macintosh +#define NSLDAPI_LBER_SOCKET_IS_PTR +#endif + +#define OLD_LBER_SEQUENCE 0x10 /* w/o constructed bit - broken */ +#define OLD_LBER_SET 0x11 /* w/o constructed bit - broken */ + +#ifndef _IFP +#define _IFP +typedef int (LDAP_C LDAP_CALLBACK *IFP)(); +#endif + +typedef struct seqorset { + ber_len_t sos_clen; + ber_tag_t sos_tag; + char *sos_first; + char *sos_ptr; + struct seqorset *sos_next; +} Seqorset; +#define NULLSEQORSET ((Seqorset *) 0) + +#define SOS_STACK_SIZE 8 /* depth of the pre-allocated sos structure stack */ + +#define MAX_TAG_SIZE (1 + sizeof(ber_int_t)) /* One byte for the length of the tag */ +#define MAX_LEN_SIZE (1 + sizeof(ber_int_t)) /* One byte for the length of the length */ +#define MAX_VALUE_PREFIX_SIZE (2 + sizeof(ber_int_t)) /* 1 byte for the tag and 1 for the len (msgid) */ +#define BER_ARRAY_QUANTITY 7 /* 0:Tag 1:Length 2:Value-prefix 3:Value 4:Value-suffix */ +#define BER_STRUCT_TAG 0 /* 5:ControlA 6:ControlB */ +#define BER_STRUCT_LEN 1 +#define BER_STRUCT_PRE 2 +#define BER_STRUCT_VAL 3 +#define BER_STRUCT_SUF 4 +#define BER_STRUCT_CO1 5 +#define BER_STRUCT_CO2 6 + +struct berelement { + ldap_x_iovec ber_struct[BER_ARRAY_QUANTITY]; /* See above */ + + char ber_tag_contents[MAX_TAG_SIZE]; + char ber_len_contents[MAX_LEN_SIZE]; + char ber_pre_contents[MAX_VALUE_PREFIX_SIZE]; + char ber_suf_contents[MAX_LEN_SIZE+1]; + + char *ber_buf; /* update the value value when writing in case realloc is called */ + char *ber_ptr; + char *ber_end; + struct seqorset *ber_sos; + ber_len_t ber_tag_len_read; + ber_tag_t ber_tag; /* Remove me someday */ + ber_len_t ber_len; /* Remove me someday */ + int ber_usertag; + char ber_options; + char *ber_rwptr; + BERTranslateProc ber_encode_translate_proc; + BERTranslateProc ber_decode_translate_proc; + int ber_flags; +#define LBER_FLAG_NO_FREE_BUFFER 1 /* don't free ber_buf */ + unsigned int ber_buf_reallocs; /* realloc counter */ + int ber_sos_stack_posn; + Seqorset ber_sos_stack[SOS_STACK_SIZE]; +}; + +#define BER_CONTENTS_STRUCT_SIZE (sizeof(ldap_x_iovec) * BER_ARRAY_QUANTITY) + +#define NULLBER ((BerElement *)NULL) + +#ifdef LDAP_DEBUG +void ber_dump( BerElement *ber, int inout ); +#endif + + + +/* + * structure for read/write I/O callback functions. + */ +struct nslberi_io_fns { + LDAP_IOF_READ_CALLBACK *lbiof_read; + LDAP_IOF_WRITE_CALLBACK *lbiof_write; +}; + + +/* + * Old structure for use with LBER_SOCKBUF_OPT_EXT_IO_FNS: + */ +struct lber_x_ext_io_fns_rev0 { + /* lbextiofn_size should always be set to LBER_X_EXTIO_FNS_SIZE */ + int lbextiofn_size; + LDAP_X_EXTIOF_READ_CALLBACK *lbextiofn_read; + LDAP_X_EXTIOF_WRITE_CALLBACK *lbextiofn_write; + struct lextiof_socket_private *lbextiofn_socket_arg; +}; +#define LBER_X_EXTIO_FNS_SIZE_REV0 sizeof(struct lber_x_ext_io_fns_rev0) + + + +struct sockbuf { + LBER_SOCKET sb_sd; + BerElement sb_ber; + int sb_naddr; /* > 0 implies using CLDAP (UDP) */ + void *sb_useaddr; /* pointer to sockaddr to use next */ + void *sb_fromaddr; /* pointer to message source sockaddr */ + void **sb_addrs; /* actually an array of pointers to + sockaddrs */ + + int sb_options; /* to support copying ber elements */ + LBER_SOCKET sb_copyfd; /* for LBER_SOCKBUF_OPT_TO_FILE* opts */ + ber_len_t sb_max_incoming; + ber_tag_t sb_valid_tag; /* valid tag to accept */ + struct nslberi_io_fns + sb_io_fns; /* classic I/O callback functions */ + + struct lber_x_ext_io_fns + sb_ext_io_fns; /* extended I/O callback functions */ +}; +#define NULLSOCKBUF ((Sockbuf *)NULL) + +/* needed by libldap, even in non-DEBUG builds */ +void ber_err_print( char *data ); + +#ifndef NSLBERI_LBER_INT_FRIEND +/* + * Everything from this point on is excluded if NSLBERI_LBER_INT_FRIEND is + * defined. The code under ../libraries/libldap defines this. + */ + +#define READBUFSIZ 8192 + +/* + * macros used to check validity of data structures and parameters + */ +#define NSLBERI_VALID_BERELEMENT_POINTER( ber ) \ + ( (ber) != NULLBER ) + +#define NSLBERI_VALID_SOCKBUF_POINTER( sb ) \ + ( (sb) != NULLSOCKBUF ) + +#define LBER_HTONL( l ) htonl( l ) +#define LBER_NTOHL( l ) ntohl( l ) + +/* function prototypes */ +#ifdef LDAP_DEBUG +void lber_bprint( char *data, int len ); +#endif +void ber_err_print( char *data ); +void *nslberi_malloc( size_t size ); +void *nslberi_calloc( size_t nelem, size_t elsize ); +void *nslberi_realloc( void *ptr, size_t size ); +void nslberi_free( void *ptr ); +int nslberi_ber_realloc( BerElement *ber, ber_len_t len ); + +/* blame: dboreham + * slapd spends much of its time doing memcpy's for the ber code. + * Most of these are single-byte, so we special-case those and speed + * things up considerably. + */ + +#ifdef sunos4 +#define THEMEMCPY( d, s, n ) bcopy( s, d, n ) +#else /* sunos4 */ +#define THEMEMCPY( d, s, n ) memmove( d, s, n ) +#endif /* sunos4 */ + +#ifdef SAFEMEMCPY +#undef SAFEMEMCPY +#define SAFEMEMCPY(d,s,n) if (1 == n) *((char*)d) = *((char*)s); else THEMEMCPY(d,s,n); +#endif + +/* + * Memory allocation done in liblber should all go through one of the + * following macros. This is so we can plug-in alternative memory + * allocators, etc. as the need arises. + */ +#define NSLBERI_MALLOC( size ) nslberi_malloc( size ) +#define NSLBERI_CALLOC( nelem, elsize ) nslberi_calloc( nelem, elsize ) +#define NSLBERI_REALLOC( ptr, size ) nslberi_realloc( ptr, size ) +#define NSLBERI_FREE( ptr ) nslberi_free( ptr ) + +/* allow the library to access the debug variable */ + +extern int lber_debug; + +#endif /* !NSLBERI_LBER_INT_FRIEND */ + + +#ifdef __cplusplus +} +#endif +#endif /* _LBERINT_H */ diff --git a/ldap/c-sdk/libraries/liblber/moz.build b/ldap/c-sdk/libraries/liblber/moz.build new file mode 100644 index 0000000000..8a344a4739 --- /dev/null +++ b/ldap/c-sdk/libraries/liblber/moz.build @@ -0,0 +1,23 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include('/ldap/ldap-sdk.mozbuild') + +Library('lber60') + +SOURCES += [ + 'bprint.c', + 'decode.c', + 'encode.c', + 'io.c', +] + +DEFINES['USE_WAITPID'] = True +DEFINES['NEEDPROTOS'] = True + +LOCAL_INCLUDES += [ + '/ldap/c-sdk/include' +] + diff --git a/ldap/c-sdk/libraries/libldap/abandon.c b/ldap/c-sdk/libraries/libldap/abandon.c new file mode 100644 index 0000000000..a514cfd895 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/abandon.c @@ -0,0 +1,303 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * abandon.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +static int do_abandon( LDAP *ld, int origid, int msgid, + LDAPControl **serverctrls, LDAPControl **clientctrls ); +static int nsldapi_send_abandon_message( LDAP *ld, LDAPConn *lc, + BerElement *ber, int abandon_msgid ); + +/* + * ldap_abandon - perform an ldap abandon operation. Parameters: + * + * ld LDAP descriptor + * msgid The message id of the operation to abandon + * + * ldap_abandon returns 0 if everything went ok, -1 otherwise. + * + * Example: + * ldap_abandon( ld, msgid ); + */ +int +LDAP_CALL +ldap_abandon( LDAP *ld, int msgid ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, "4e65747363617065\n", msgid, 0, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, "466f726576657221\n", msgid, 0, 0 ); + + if ( ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS ) { + return( 0 ); + } + + return( -1 ); +} + + +/* + * LDAPv3 extended abandon. + * Returns an LDAP error code. + */ +int +LDAP_CALL +ldap_abandon_ext( LDAP *ld, int msgid, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + rc = do_abandon( ld, msgid, msgid, serverctrls, clientctrls ); + + /* + * XXXmcs should use cache function pointers to hook in memcache + */ + ldap_memcache_abandon( ld, msgid ); + + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + + return( rc ); +} + + +/* + * Abandon all outstanding requests for msgid (included child requests + * spawned when chasing referrals). This function calls itself recursively. + * No locking is done is this function so it must be done by the caller. + * Returns an LDAP error code and sets it in LDAP *ld as well + */ +static int +do_abandon( LDAP *ld, int origid, int msgid, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + BerElement *ber; + int i, bererr, lderr, sendabandon; + LDAPRequest *lr = NULL; + + /* + * An abandon request looks like this: + * AbandonRequest ::= MessageID + */ + LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n", + origid, msgid, 0 ); + + /* optimistic */ + lderr = LDAP_SUCCESS; + + /* + * Find the request that we are abandoning. Don't send an + * abandon message unless there is something to abandon. + */ + sendabandon = 0; + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + if ( lr->lr_msgid == msgid ) { /* this message */ + if ( origid == msgid && lr->lr_parent != NULL ) { + /* don't let caller abandon child requests! */ + lderr = LDAP_PARAM_ERROR; + goto set_errorcode_and_return; + } + if ( lr->lr_status == LDAP_REQST_INPROGRESS ) { + /* + * We only need to send an abandon message if + * the request is in progress. + */ + sendabandon = 1; + } + break; + } + if ( lr->lr_origid == msgid ) { /* child: abandon it */ + (void)do_abandon( ld, msgid, lr->lr_msgid, + serverctrls, clientctrls ); + /* we ignore errors from child abandons... */ + } + } + + if ( ldap_msgdelete( ld, msgid ) == 0 ) { + /* we had all the results and deleted them */ + goto set_errorcode_and_return; + } + + if ( lr != NULL && sendabandon ) { + /* create a message to send */ + if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) == + LDAP_SUCCESS ) { + int abandon_msgid; + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + abandon_msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); +#ifdef CLDAP + if ( ld->ld_dbp->sb_naddr > 0 ) { + bererr = ber_printf( ber, "{isti", + abandon_msgid, ld->ld_cldapdn, + LDAP_REQ_ABANDON, msgid ); + } else { +#endif /* CLDAP */ + bererr = ber_printf( ber, "{iti", + abandon_msgid, LDAP_REQ_ABANDON, msgid ); +#ifdef CLDAP + } +#endif /* CLDAP */ + + if ( bererr == -1 || + ( lderr = nsldapi_put_controls( ld, serverctrls, + 1, ber )) != LDAP_SUCCESS ) { + lderr = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + } else { + /* try to send the message */ + lderr = nsldapi_send_abandon_message( ld, + lr->lr_conn, ber, abandon_msgid ); + } + } + } + + if ( lr != NULL ) { + /* + * Always call nsldapi_free_connection() so that the connection's + * ref count is correctly decremented. It is OK to always pass + * 1 for the "unbind" parameter because doing so will only affect + * connections that resulted from a child request (because the + * default connection's ref count never goes to zero). + */ + nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL, + 0 /* do not force */, 1 /* send unbind before closing */ ); + + /* + * Free the entire request chain if we finished abandoning everything. + */ + if ( origid == msgid ) { + nsldapi_free_request( ld, lr, 0 ); + } + } + + /* + * Record the abandoned message ID (used to discard any server responses + * that arrive later). + */ + LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK ); + if ( ld->ld_abandoned == NULL ) { + if ( (ld->ld_abandoned = (int *)NSLDAPI_MALLOC( 2 + * sizeof(int) )) == NULL ) { + lderr = LDAP_NO_MEMORY; + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + goto set_errorcode_and_return; + } + i = 0; + } else { + for ( i = 0; ld->ld_abandoned[i] != -1; i++ ) + ; /* NULL */ + if ( (ld->ld_abandoned = (int *)NSLDAPI_REALLOC( (char *) + ld->ld_abandoned, (i + 2) * sizeof(int) )) == NULL ) { + lderr = LDAP_NO_MEMORY; + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + goto set_errorcode_and_return; + } + } + ld->ld_abandoned[i] = msgid; + ld->ld_abandoned[i + 1] = -1; + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + +set_errorcode_and_return: + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + return( lderr ); +} + +/* + * Try to send the abandon message that is encoded in ber. Returns an + * LDAP result code. + */ +static int +nsldapi_send_abandon_message( LDAP *ld, LDAPConn *lc, BerElement *ber, + int abandon_msgid ) +{ + int lderr = LDAP_SUCCESS; + int err = 0; + + err = nsldapi_send_ber_message( ld, lc->lconn_sb, + ber, 1 /* free ber */, 0 /* will not handle EPIPE */ ); + if ( err == -2 ) { + /* + * "Would block" error. Queue the abandon as + * a pending request. + */ + LDAPRequest *lr; + + lr = nsldapi_new_request( lc, ber, abandon_msgid, + 0 /* no response expected */ ); + if ( lr == NULL ) { + lderr = LDAP_NO_MEMORY; + ber_free( ber, 1 ); + } else { + lr->lr_status = LDAP_REQST_WRITING; + nsldapi_queue_request_nolock( ld, lr ); + ++lc->lconn_pending_requests; + nsldapi_iostatus_interest_write( ld, + lc->lconn_sb ); + } + } else if ( err != 0 ) { + /* + * Fatal error (not a "would block" error). + */ + lderr = LDAP_SERVER_DOWN; + ber_free( ber, 1 ); + } + + return( lderr ); +} diff --git a/ldap/c-sdk/libraries/libldap/add.c b/ldap/c-sdk/libraries/libldap/add.c new file mode 100644 index 0000000000..0cb5dbfb5f --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/add.c @@ -0,0 +1,225 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * add.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_add - initiate an ldap add operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the entry to add + * mods List of attributes for the entry. This is a null- + * terminated array of pointers to LDAPMod structures. + * only the type and values in the structures need be + * filled in. + * + * Example: + * LDAPMod *attrs[] = { + * { 0, "cn", { "babs jensen", "babs", 0 } }, + * { 0, "sn", { "jensen", 0 } }, + * { 0, "objectClass", { "person", 0 } }, + * 0 + * } + * msgid = ldap_add( ld, dn, attrs ); + */ +int +LDAP_CALL +ldap_add( LDAP *ld, const char *dn, LDAPMod **attrs ) +{ + int msgid; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_add\n", 0, 0, 0 ); + + if ( ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid ) + == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + + +/* + * LDAPv3 extended add. + * Returns an LDAP error code. + */ +int +LDAP_CALL +ldap_add_ext( LDAP *ld, const char *dn, LDAPMod **attrs, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ) +{ + BerElement *ber; + int i, rc, lderr; + + /* + * An add request looks like this: + * AddRequest ::= SEQUENCE { + * entry DistinguishedName, + * attrs SEQUENCE OF SEQUENCE { + * type AttributeType, + * values SET OF AttributeValue + * } + * } + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_add_ext\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( !NSLDAPI_VALID_LDAPMESSAGE_POINTER( msgidp )) + { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + if ( !NSLDAPI_VALID_NONEMPTY_LDAPMOD_ARRAY( attrs ) + || msgidp == NULL ) { + lderr = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + return( lderr ); + } + + if ( dn == NULL ) { + dn = ""; + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* see if we should add to the cache */ + if ( ld->ld_cache_on && ld->ld_cache_add != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_add)( ld, *msgidp, LDAP_REQ_ADD, dn, + attrs )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* create a message to send */ + if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( lderr ); + } + + if ( ber_printf( ber, "{it{s{", *msgidp, LDAP_REQ_ADD, dn ) + == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + /* for each attribute in the entry... */ + for ( i = 0; attrs[i] != NULL; i++ ) { + if ( ( attrs[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + rc = ber_printf( ber, "{s[V]}", attrs[i]->mod_type, + attrs[i]->mod_bvalues ); + } else { + rc = ber_printf( ber, "{s[v]}", attrs[i]->mod_type, + attrs[i]->mod_values ); + } + if ( rc == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + } + + if ( ber_printf( ber, "}}" ) == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + if (( lderr = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( lderr ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_ADD, + (char *) dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +int +LDAP_CALL +ldap_add_s( LDAP *ld, const char *dn, LDAPMod **attrs ) +{ + return( ldap_add_ext_s( ld, dn, attrs, NULL, NULL )); +} + +int LDAP_CALL +ldap_add_ext_s( LDAP *ld, const char *dn, LDAPMod **attrs, + LDAPControl **serverctrls, LDAPControl **clientctrls ) +{ + int err, msgid; + LDAPMessage *res; + + if (( err = ldap_add_ext( ld, dn, attrs, serverctrls, clientctrls, + &msgid )) != LDAP_SUCCESS ) { + return( err ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, &res ) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/authzidctrl.c b/ldap/c-sdk/libraries/libldap/authzidctrl.c new file mode 100644 index 0000000000..69b776cdc4 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/authzidctrl.c @@ -0,0 +1,157 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): abobrov@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_create_authzid_control: + +Parameters are + +ld LDAP pointer to the desired connection + +ctl_iscritical Indicates whether the control is critical of not. + If this field is non-zero, the operation will only be + carried out if the control is recognized by the server + and/or client + +ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_authzid_control ( + LDAP *ld, + const char ctl_iscritical, + LDAPControl **ctrlp + ) +{ + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_AUTHZID_REQ, + NULL, 0, ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + +/* ldap_parse_authzid_control: + +Parameters are + +ld LDAP pointer to the desired connection + +ctrlp An array of controls obtained from calling + ldap_parse_result on the set of results + returned by the server + +authzid authorization identity, as defined in + RFC 2829, section 9. +*/ + +int +LDAP_CALL +ldap_parse_authzid_control ( + LDAP *ld, + LDAPControl **ctrlp, + char **authzid + ) +{ + int i, foundAUTHZIDControl; + char *authzidp = NULL; + LDAPControl *AUTHZIDCtrlp = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) { + return( LDAP_PARAM_ERROR ); + } + + /* find the control in the list of controls if it exists */ + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + foundAUTHZIDControl = 0; + for ( i = 0; (( ctrlp[i] != NULL ) && ( !foundAUTHZIDControl )); i++ ) { + foundAUTHZIDControl = !strcmp( ctrlp[i]->ldctl_oid, + LDAP_CONTROL_AUTHZID_RES ); + } + + /* + * The control is only included in a bind response if the resultCode + * for the bind operation is success. + */ + if ( !foundAUTHZIDControl ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } else { + /* let local var point to the control */ + AUTHZIDCtrlp = ctrlp[i-1]; + } + + /* + * If the bind request succeeded and resulted in an identity (not anonymous), + * the controlValue contains the authorization identity (authzid), as + * defined in [AUTH] section 9, granted to the requestor. If the bind + * request resulted in an anonymous association, the controlValue field + * is a string of zero length. If the bind request resulted in more + * than one authzid, the primary authzid is returned in the controlValue + * field. + */ + if ( AUTHZIDCtrlp && AUTHZIDCtrlp->ldctl_value.bv_val && + AUTHZIDCtrlp->ldctl_value.bv_len ) { + authzidp = ( (char *)NSLDAPI_MALLOC( + ( AUTHZIDCtrlp->ldctl_value.bv_len + 1 ) ) ); + if ( authzidp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + STRLCPY( authzidp, AUTHZIDCtrlp->ldctl_value.bv_val, + ( AUTHZIDCtrlp->ldctl_value.bv_len + 1 ) ); + *authzid = authzidp; + } else { + authzid = NULL; + } + + return( LDAP_SUCCESS ); +} diff --git a/ldap/c-sdk/libraries/libldap/bind.c b/ldap/c-sdk/libraries/libldap/bind.c new file mode 100644 index 0000000000..8b5504be31 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/bind.c @@ -0,0 +1,170 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * bind.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_bind - bind to the ldap server. The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. The msgid of the bind request is returned on success, + * -1 if there's trouble. Note, the kerberos support assumes the user already + * has a valid tgt for now. ldap_result() should be called to find out the + * outcome of the bind request. + * + * Example: + * ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret", + * LDAP_AUTH_SIMPLE ) + */ + +int +LDAP_CALL +ldap_bind( LDAP *ld, const char *dn, const char *passwd, int authmethod ) +{ + /* + * The bind request looks like this: + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * } + * } + * all wrapped up in an LDAPMessage sequence. + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_bind\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); + } + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind( ld, dn, passwd ) ); + + default: + LDAP_SET_LDERRNO( ld, LDAP_AUTH_UNKNOWN, NULL, NULL ); + return( -1 ); + } +} + +/* + * ldap_bind_s - bind to the ldap server. The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. This routine just calls whichever bind routine is + * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or + * some other error indication). Note, the kerberos support assumes the + * user already has a valid tgt for now. + * + * Examples: + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret", LDAP_AUTH_SIMPLE ) + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * NULL, LDAP_AUTH_KRBV4 ) + */ +int +LDAP_CALL +ldap_bind_s( LDAP *ld, const char *dn, const char *passwd, int authmethod ) +{ + int err; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_bind_s\n", 0, 0, 0 ); + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind_s( ld, dn, passwd ) ); + + default: + err = LDAP_AUTH_UNKNOWN; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } +} + + +void +LDAP_CALL +ldap_set_rebind_proc( LDAP *ld, LDAP_REBINDPROC_CALLBACK *rebindproc, + void *arg ) +{ + if ( ld == NULL ) { + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + ld = &nsldapi_ld_defaults; + } + + if ( NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + ld->ld_rebind_fn = rebindproc; + ld->ld_rebind_arg = arg; + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + } +} + + +/* + * return a pointer to the bind DN for the default connection (a copy is + * not made). If there is no bind DN available, NULL is returned. + */ +char * +nsldapi_get_binddn( LDAP *ld ) +{ + char *binddn; + + binddn = NULL; /* default -- assume they are not bound */ + + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + if ( NULL != ld->ld_defconn && LDAP_CONNST_CONNECTED == + ld->ld_defconn->lconn_status && ld->ld_defconn->lconn_bound ) { + if (( binddn = ld->ld_defconn->lconn_binddn ) == NULL ) { + binddn = ""; + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + + return( binddn ); +} diff --git a/ldap/c-sdk/libraries/libldap/cache.c b/ldap/c-sdk/libraries/libldap/cache.c new file mode 100644 index 0000000000..54195e5fe3 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/cache.c @@ -0,0 +1,145 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993 The Regents of the University of Michigan. + * All rights reserved. + */ +/* + * cache.c - generic caching support for LDAP + */ + +#include "ldap-int.h" + +/* + * ldap_cache_flush - flush part of the LDAP cache. returns an + * ldap error code (LDAP_SUCCESS, LDAP_NO_SUCH_OBJECT, etc.). + */ + +int +LDAP_CALL +ldap_cache_flush( LDAP *ld, const char *dn, const char *filter ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( dn == NULL ) { + dn = ""; + } + + return( (ld->ld_cache_flush)( ld, dn, filter ) ); +} + +/* + * nsldapi_add_result_to_cache - add an ldap entry we just read off the network + * to the ldap cache. this routine parses the ber for the entry and + * constructs the appropriate add request. this routine calls the + * cache add routine to actually add the entry. + */ + +void +nsldapi_add_result_to_cache( LDAP *ld, LDAPMessage *m ) +{ + char *dn; + LDAPMod **mods; + int i, max, rc; + char *a; + BerElement *ber; + char buf[50]; + struct berval bv; + struct berval *bvp[2]; + + LDAPDebug( LDAP_DEBUG_TRACE, "=> nsldapi_add_result_to_cache id %d type %d\n", + m->lm_msgid, m->lm_msgtype, 0 ); + if ( m->lm_msgtype != LDAP_RES_SEARCH_ENTRY || + ld->ld_cache_add == NULL ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_add_result_to_cache not added\n", 0, 0, 0 ); + return; + } + +#define GRABSIZE 5 + + dn = ldap_get_dn( ld, m ); + mods = (LDAPMod **)NSLDAPI_MALLOC( GRABSIZE * sizeof(LDAPMod *) ); + max = GRABSIZE; + for ( i = 0, a = ldap_first_attribute( ld, m, &ber ); a != NULL; + a = ldap_next_attribute( ld, m, ber ), i++ ) { + if ( i == (max - 1) ) { + max += GRABSIZE; + mods = (LDAPMod **)NSLDAPI_REALLOC( mods, + sizeof(LDAPMod *) * max ); + } + + mods[i] = (LDAPMod *)NSLDAPI_CALLOC( 1, sizeof(LDAPMod) ); + mods[i]->mod_op = LDAP_MOD_BVALUES; + mods[i]->mod_type = a; + mods[i]->mod_bvalues = ldap_get_values_len( ld, m, a ); + } + if ( ber != NULL ) { + ber_free( ber, 0 ); + } + if (( rc = LDAP_GET_LDERRNO( ld, NULL, NULL )) != LDAP_SUCCESS ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_add_result_to_cache error: failed to construct mod list (%s)\n", + ldap_err2string( rc ), 0, 0 ); + ldap_mods_free( mods, 1 ); + return; + } + + /* update special cachedtime attribute */ + if ( i == (max - 1) ) { + max++; + mods = (LDAPMod **)NSLDAPI_REALLOC( mods, + sizeof(LDAPMod *) * max ); + } + mods[i] = (LDAPMod *)NSLDAPI_CALLOC( 1, sizeof(LDAPMod) ); + mods[i]->mod_op = LDAP_MOD_BVALUES; + mods[i]->mod_type = "cachedtime"; + sprintf( buf, "%ld", time( NULL ) ); + bv.bv_val = buf; + bv.bv_len = strlen( buf ); + bvp[0] = &bv; + bvp[1] = NULL; + mods[i]->mod_bvalues = bvp; + mods[++i] = NULL; + + /* msgid of -1 means don't send the result */ + rc = (ld->ld_cache_add)( ld, -1, m->lm_msgtype, dn, mods ); + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_add_result_to_cache added (rc %d)\n", rc, 0, 0 ); +} diff --git a/ldap/c-sdk/libraries/libldap/charray.c b/ldap/c-sdk/libraries/libldap/charray.c new file mode 100644 index 0000000000..043f99e8aa --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/charray.c @@ -0,0 +1,248 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* charray.c - routines for dealing with char * arrays */ + + +#include "ldap-int.h" + +/* + * Add s at the end of the array of strings *a. + * Return 0 for success, -1 for failure. + */ +int +LDAP_CALL +ldap_charray_add( + char ***a, + char *s +) +{ + int n; + + if ( *a == NULL ) { + *a = (char **)NSLDAPI_MALLOC( 2 * sizeof(char *) ); + if ( *a == NULL ) { + return -1; + } + n = 0; + } else { + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + + *a = (char **)NSLDAPI_REALLOC( (char *) *a, + (n + 2) * sizeof(char *) ); + if ( *a == NULL ) { + return -1; + } + } + + (*a)[n++] = s; + (*a)[n] = NULL; + return 0; +} + +/* + * Add array of strings s at the end of the array of strings *a. + * Return 0 for success, -1 for failure. + */ +int +LDAP_CALL +ldap_charray_merge( + char ***a, + char **s +) +{ + int i, n, nn; + + if ( (s == NULL) || (s[0] == NULL) ) + return 0; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + for ( nn = 0; s[nn] != NULL; nn++ ) { + ; /* NULL */ + } + + *a = (char **)NSLDAPI_REALLOC( (char *) *a, + (n + nn + 1) * sizeof(char *) ); + if ( *a == NULL ) { + return -1; + } + + for ( i = 0; i < nn; i++ ) { + (*a)[n + i] = s[i]; + } + (*a)[n + nn] = NULL; + return 0; +} + +void +LDAP_CALL +ldap_charray_free( char **array ) +{ + char **a; + + if ( array == NULL ) { + return; + } + + for ( a = array; *a != NULL; a++ ) { + if ( *a != NULL ) { + NSLDAPI_FREE( *a ); + } + } + NSLDAPI_FREE( (char *) array ); +} + +int +LDAP_CALL +ldap_charray_inlist( + char **a, + char *s +) +{ + int i; + + if ( a == NULL ) + return( 0 ); + + for ( i = 0; a[i] != NULL; i++ ) { + if ( strcasecmp( s, a[i] ) == 0 ) { + return( 1 ); + } + } + + return( 0 ); +} + +/* + * Duplicate the array of strings a, return NULL upon any memory failure. + */ +char ** +LDAP_CALL +ldap_charray_dup( char **a ) +{ + int i; + char **new; + + for ( i = 0; a[i] != NULL; i++ ) + ; /* NULL */ + + new = (char **)NSLDAPI_MALLOC( (i + 1) * sizeof(char *) ); + if ( new == NULL ) { + return NULL; + } + + for ( i = 0; a[i] != NULL; i++ ) { + new[i] = nsldapi_strdup( a[i] ); + if ( new[i] == NULL ) { + int j; + + for ( j = 0; j < i; j++ ) + NSLDAPI_FREE( new[j] ); + NSLDAPI_FREE( new ); + return NULL; + } + } + new[i] = NULL; + + return( new ); +} + +/* + * Tokenize the string str, return NULL upon any memory failure. + * XXX: on many platforms this function is not thread safe because it + * uses strtok(). + */ +char ** +LDAP_CALL +ldap_str2charray( char *str, char *brkstr ) + /* This implementation fails if brkstr contains multibyte characters. + But it works OK if str is UTF-8 and brkstr is 7-bit ASCII. + */ +{ + char **res; + char *s; + int i; +#ifdef HAVE_STRTOK_R /* defined in portable.h */ + char *lasts; +#endif + + i = 1; + for ( s = str; *s; s++ ) { + if ( strchr( brkstr, *s ) != NULL ) { + i++; + } + } + + res = (char **)NSLDAPI_MALLOC( (i + 1) * sizeof(char *) ); + if ( res == NULL ) { + return NULL; + } + i = 0; + for ( s = STRTOK( str, brkstr, &lasts ); s != NULL; s = STRTOK( NULL, + brkstr, &lasts ) ) { + res[i++] = nsldapi_strdup( s ); + if ( res[i - 1] == NULL ) { + int j; + + for ( j = 0; j < (i - 1); j++ ) + NSLDAPI_FREE( res[j] ); + NSLDAPI_FREE( res ); + return NULL; + } + } + res[i] = NULL; + + return( res ); +} + +int +LDAP_CALL +ldap_charray_position( char **a, char *s ) +{ + int i; + + for ( i = 0; a[i] != NULL; i++ ) { + if ( strcasecmp( s, a[i] ) == 0 ) { + return( i ); + } + } + + return( -1 ); +} diff --git a/ldap/c-sdk/libraries/libldap/charset.c b/ldap/c-sdk/libraries/libldap/charset.c new file mode 100644 index 0000000000..23bad239be --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/charset.c @@ -0,0 +1,1845 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * charset.c + */ + +#include "ldap-int.h" + +#ifdef STR_TRANSLATION + +void +ldap_set_string_translators( LDAP *ld, BERTranslateProc encode_proc, + BERTranslateProc decode_proc ) +{ + if ( ld == NULL ) { + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + ld = &nsldapi_ld_defaults; + } + + if ( NSLDAPI_VALID_LDAP_POINTER( ld )) { + ld->ld_lber_encode_translate_proc = encode_proc; + ld->ld_lber_decode_translate_proc = decode_proc; + } +} + + +void +ldap_enable_translation( LDAP *ld, LDAPMessage *entry, int enable ) +{ + char *optionsp; + + if ( ld == NULL ) { + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + ld = &nsldapi_ld_defaults; + } + + optionsp = ( entry == NULLMSG ) ? &ld->ld_lberoptions : + &entry->lm_ber->ber_options; + + if ( enable ) { + *optionsp |= LBER_OPT_TRANSLATE_STRINGS; + } else { + *optionsp &= ~LBER_OPT_TRANSLATE_STRINGS; + } +} + + +int +ldap_translate_from_t61( LDAP *ld, char **bufp, unsigned long *lenp, + int free_input ) +{ + if ( ld->ld_lber_decode_translate_proc == NULL ) { + return( LDAP_SUCCESS ); + } + + return( (*ld->ld_lber_decode_translate_proc)( bufp, lenp, free_input )); +} + + +int +ldap_translate_to_t61( LDAP *ld, char **bufp, unsigned long *lenp, + int free_input ) +{ + if ( ld->ld_lber_encode_translate_proc == NULL ) { + return( LDAP_SUCCESS ); + } + + return( (*ld->ld_lber_encode_translate_proc)( bufp, lenp, free_input )); +} + + +/* + ** Character translation routine notes: + * + * On entry: bufp points to a "string" to be converted (not necessarily + * zero-terminated) and buflenp points to the length of the buffer. + * + * On exit: bufp should point to a malloc'd result. If free_input is + * non-zero then the original bufp will be freed. *buflenp should be + * set to the new length. Zero bytes in the input buffer must be left + * as zero bytes. + * + * Return values: any ldap error code (LDAP_SUCCESS if all goes well). + */ + + +#ifdef LDAP_CHARSET_8859 + +#if LDAP_CHARSET_8859 == 88591 +#define ISO_8859 1 +#elif LDAP_CHARSET_8859 == 88592 +#define ISO_8859 2 +#elif LDAP_CHARSET_8859 == 88593 +#define ISO_8859 3 +#elif LDAP_CHARSET_8859 == 88594 +#define ISO_8859 4 +#elif LDAP_CHARSET_8859 == 88595 +#define ISO_8859 5 +#elif LDAP_CHARSET_8859 == 88596 +#define ISO_8859 6 +#elif LDAP_CHARSET_8859 == 88597 +#define ISO_8859 7 +#elif LDAP_CHARSET_8859 == 88598 +#define ISO_8859 8 +#elif LDAP_CHARSET_8859 == 88599 +#define ISO_8859 9 +#elif LDAP_CHARSET_8859 == 885910 +#define ISO_8859 10 +#else +#define ISO_8859 0 +#endif + +/* + * the following ISO_8859 to/afrom T.61 character set translation code is + * based on the code found in Enrique Silvestre Mora's iso-t61.c, found + * as part of this package: + * ftp://pereiii.uji.es/pub/uji-ftp/unix/ldap/iso-t61.translation.tar.Z + * Enrique is now (10/95) at this address: enrique.silvestre@uv.es + * + * changes made by mcs@umich.edu 12 October 1995: + * Change calling conventions of iso8859_t61() and t61_iso8859() to + * match libldap conventions; rename to ldap_8859_to_t61() and + * ldap_t61_to_8859(). + * Change conversion routines to deal with non-zero terminated strings. + * ANSI-ize functions and include prototypes. + */ + +/* iso-t61.c - ISO-T61 translation routines (version: 0.2.1, July-1994) */ +/* + * Copyright (c) 1994 Enrique Silvestre Mora, Universitat Jaume I, Spain. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the Universitat Jaume I. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. +*/ + + +#include +#include +#include + +/* Character set used: ISO 8859-1, ISO 8859-2, ISO 8859-3, ... */ +/* #define ISO_8859 1 */ + +#ifndef ISO_8859 +# define ISO_8859 0 +#endif + +typedef unsigned char Byte; +typedef struct { Byte a, b; } Couple; + +#ifdef NEEDPROTOS +static Byte *c_to_hh( Byte *o, Byte c ); +static Byte *c_to_cc( Byte *o, Couple *cc, Byte c ); +static int hh_to_c( Byte *h ); +static Byte *cc_to_t61( Byte *o, Byte *s ); +#else /* NEEDPROTOS */ +static Byte *c_to_hh(); +static Byte *c_to_cc(); +static int hh_to_c(); +static Byte *cc_to_t61(); +#endif /* NEEDPROTOS */ + +/* + Character choosed as base in diacritics alone: NO-BREAK SPACE. + (The standard say it must be a blank space, 0x20.) +*/ +#define ALONE 0xA0 + +static Couple diacritic[16] = { +#if (ISO_8859 == 1) || (ISO_8859 == 9) + {0,0}, {'`',0}, {0xb4,0}, {'^',0}, + {'~',0}, {0xaf,0}, {'(',ALONE}, {'.',ALONE}, + {0xa8,0}, {0,0}, {'0',ALONE}, {0xb8,0}, + {0,0}, {'"',ALONE}, {';',ALONE}, {'<',ALONE}, +#elif (ISO_8859 == 2) + {0,0}, {'`',0}, {0xb4,0}, {'^',0}, + {'~',0}, {'-',ALONE}, {0xa2,0}, {0xff,0}, + {0xa8,0}, {0,0}, {'0',ALONE}, {0xb8,0}, + {0,0}, {0xbd,0}, {0xb2,0}, {0xb7,0} +#elif (ISO_8859 == 3) + {0,0}, {'`',0}, {0xb4,0}, {'^',0}, + {'~',0}, {'-',ALONE}, {0xa2,0}, {0xff,0}, + {0xa8,0}, {0,0}, {'0',ALONE}, {0xb8,0}, + {0,0}, {'"',ALONE}, {';',ALONE}, {'<',ALONE} +#elif (ISO_8859 == 4) + {0,0}, {'`',0}, {0xb4,0}, {'^',0}, + {'~',0}, {0xaf,0}, {'(',ALONE}, {0xff,0}, + {0xa8,0}, {0,0}, {'0',ALONE}, {0xb8,0}, + {0,0}, {'"',ALONE}, {0xb2,0}, {0xb7,0} +#else + {0,0}, {'`',0}, {'\'',ALONE}, {'^',0}, + {'~',0}, {'-',ALONE}, {'(',ALONE}, {'.',ALONE}, + {':',ALONE}, {0,0}, {'0',ALONE}, {',',ALONE}, + {0,0}, {'"',ALONE}, {';',ALONE}, {'<',ALONE} +#endif +}; + +/* + --- T.61 (T.51) letters with diacritics: conversion to ISO 8859-n ----- + A, C, D, E, G, H, I, J, K, + L, N, O, R, S, T, U, W, Y, Z. + ----------------------------------------------------------------------- +*/ +static int letter_w_diacritic[16][38] = { +#if (ISO_8859 == 1) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc0,0, 0, 0xc8,0, 0, 0xcc,0, 0, + 0, 0, 0xd2,0, 0, 0, 0xd9,0, 0, 0, + 0xe0,0, 0, 0xe8,0, 0, 0xec,0, 0, + 0, 0, 0xf2,0, 0, 0, 0xf9,0, 0, 0, + 0xc1,-1, 0, 0xc9,0, 0, 0xcd,0, 0, + -1, -1, 0xd3,-1, -1, 0, 0xda,0, 0xdd,-1, + 0xe1,-1, 0, 0xe9,0, 0, 0xed,0, 0, + -1, -1, 0xf3,-1, -1, 0, 0xfa,0, 0xfd,-1, + 0xc2,-1, 0, 0xca,-1, -1, 0xce,-1, 0, + 0, 0, 0xd4,0, -1, 0, 0xdb,-1, -1, 0, + 0xe2,-1, 0, 0xea,-1, -1, 0xee,-1, 0, + 0, 0, 0xf4,0, -1, 0, 0xfb,-1, -1, 0, + 0xc3,0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xd1,0xd5,0, 0, 0, -1, 0, 0, 0, + 0xe3,0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xf1,0xf5,0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, -1, -1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0xc4,0, 0, 0xcb,0, 0, 0xcf,0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, 0xef,0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, 0xff,0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xe5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, 0xc7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, + 0, 0xe7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1 +#elif (ISO_8859 == 2) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xc1,0xc6,0, 0xc9,0, 0, 0xcd,0, 0, + 0xc5,0xd1,0xd3,0xc0,0xa6,0, 0xda,0, 0xdd,0xac, + 0xe1,0xe6,0, 0xe9,0, 0, 0xed,0, 0, + 0xe5,0xf1,0xf3,0xe0,0xb6,0, 0xfa,0, 0xfd,0xbc, + 0xc2,-1, 0, -1, -1, -1, 0xce,-1, 0, + 0, 0, 0xd4,0, -1, 0, -1, -1, -1, 0, + 0xe2,-1, 0, -1, -1, -1, 0xee,-1, 0, + 0, 0, 0xf4,0, -1, 0, -1, -1, -1, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xc3,0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xe3,0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, -1, -1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xaf, + 0, -1, 0, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbf, + 0xc4,0, 0, 0xcb,0, 0, -1, 0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, -1, 0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0xd9,0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0xf9,0, 0, 0, + 0, 0xc7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xaa,0xde,0, 0, 0, 0, + 0, 0xe7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xba,0xfe,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xd5,0, 0, 0, 0xdb,0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xf5,0, 0, 0, 0xfb,0, 0, 0, + 0xa1,0, 0, 0xca,0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xb1,0, 0, 0xea,0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, 0xc8,0xcf,0xcc,0, 0, 0, 0, 0, + 0xa5,0xd2,0, 0xd8,0xa9,0xab,0, 0, 0, 0xae, + 0, 0xe8,0xef,0xec,0, 0, 0, 0, 0, + 0xb5,0xf2,0, 0xf8,0xb9,0xbb,0, 0, 0, 0xbe +#elif (ISO_8859 == 3) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc0,0, 0, 0xc8,0, 0, 0xcc,0, 0, + 0, 0, 0xd2,0, 0, 0, 0xd9,0, 0, 0, + 0xe0,0, 0, 0xe8,0, 0, 0xec,0, 0, + 0, 0, 0xf2,0, 0, 0, 0xf9,0, 0, 0, + 0xc1,-1, 0, 0xc9,0, 0, 0xcd,0, 0, + -1, -1, 0xd3,-1, -1, 0, 0xda,0, -1, -1, + 0xe1,-1, 0, 0xe9,0, 0, 0xed,0, 0, + -1, -1, 0xf3,-1, -1, 0, 0xfa,0, -1, -1, + 0xc2,0xc6,0, 0xca,0xd8,0xa6,0xce,0xac,0, + 0, 0, 0xd4,0, 0xde,0, 0xdb,-1, -1, 0, + 0xe2,0xe6,0, 0xea,0xf8,0xb6,0xee,0xbc,0, + 0, 0, 0xf4,0, 0xfe,0, 0xfb,-1, -1, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xd1,-1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xf1,-1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0xab,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0xdd,0, 0, 0, + -1, 0, 0, 0, 0xbb,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0xfd,0, 0, 0, + 0, 0xc5,0, -1, 0xd5,0, 0xa9,0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xaf, + 0, 0xe5,0, -1, 0xf5,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbf, + 0xc4,0, 0, 0xcb,0, 0, 0xcf,0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, 0xef,0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, 0xc7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xaa,-1, 0, 0, 0, 0, + 0, 0xe7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xba,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1 +#elif (ISO_8859 == 4) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xc1,-1, 0, 0xc9,0, 0, 0xcd,0, 0, + -1, -1, -1, -1, -1, 0, 0xda,0, -1, -1, + 0xe1,-1, 0, 0xe9,0, 0, 0xed,0, 0, + -1, -1, -1, -1, -1, 0, 0xfa,0, -1, -1, + 0xc2,-1, 0, -1, -1, -1, 0xce,-1, 0, + 0, 0, 0xd4,0, -1, 0, 0xdb,-1, -1, 0, + 0xe2,-1, 0, -1, -1, -1, 0xee,-1, 0, + 0, 0, 0xf4,0, -1, 0, 0xfb,-1, -1, 0, + 0xc3,0, 0, 0, 0, 0, 0xa5,0, 0, + 0, -1, 0xd5,0, 0, 0, 0xdd,0, 0, 0, + 0xe3,0, 0, 0, 0, 0, 0xb5,0, 0, + 0, -1, 0xf5,0, 0, 0, 0xfd,0, 0, 0, + 0xc0,0, 0, 0xaa,0, 0, 0xcf,0, 0, + 0, 0, 0xd2,0, 0, 0, 0xde,0, 0, 0, + 0xe0,0, 0, 0xba,0, 0, 0xef,0, 0, + 0, 0, 0xf2,0, 0, 0, 0xfe,0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, 0xcc,-1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, 0xec,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0xc4,0, 0, 0xcb,0, 0, -1, 0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, -1, 0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xe5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, 0, 0xab,0, 0, 0, 0xd3, + 0xa6,0xd1,0, 0xa3,-1, -1, 0, 0, 0, 0, + 0, -1, 0, 0, 0xbb,0, 0, 0, 0xf3, + 0xb6,0xf1,0, 0xb3,-1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xa1,0, 0, 0xca,0, 0, 0xc7,0, 0, + 0, 0, 0, 0, 0, 0, 0xd9,0, 0, 0, + 0xb1,0, 0, 0xea,0, 0, 0xe7,0, 0, + 0, 0, 0, 0, 0, 0, 0xf9,0, 0, 0, + 0, 0xc8,-1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, 0xa9,-1, 0, 0, 0, 0xae, + 0, 0xe8,-1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, 0xb9,-1, 0, 0, 0, 0xbe +#elif (ISO_8859 == 9) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc0,0, 0, 0xc8,0, 0, 0xcc,0, 0, + 0, 0, 0xd2,0, 0, 0, 0xd9,0, 0, 0, + 0xe0,0, 0, 0xe8,0, 0, -1, 0, 0, + 0, 0, 0xf2,0, 0, 0, 0xf9,0, 0, 0, + 0xc1,-1, 0, 0xc9,0, 0, 0xcd,0, 0, + -1, -1, 0xd3,-1, -1, 0, 0xda,0, -1, -1, + 0xe1,-1, 0, 0xe9,0, 0, 0xed,0, 0, + -1, -1, 0xf3,-1, -1, 0, 0xfa,0, -1, -1, + 0xc2,-1, 0, 0xca,-1, -1, 0xce,-1, 0, + 0, 0, 0xd4,0, -1, 0, 0xdb,-1, -1, 0, + 0xe2,-1, 0, -1, -1, -1, 0xee,-1, 0, + 0, 0, 0xf4,0, -1, 0, 0xfb,-1, -1, 0, + 0xc3,0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xd1,0xd5,0, 0, 0, -1, 0, 0, 0, + 0xe3,0, 0, 0, 0, 0, -1, 0, 0, + 0, 0xf1,0xf5,0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, 0xef,0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0xd0,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0xf0,0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, -1, -1, 0, 0xdd,0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, 0xec,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0xc4,0, 0, 0xcb,0, 0, 0xcf,0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, -1, 0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, 0xff,0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xe5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, 0xc7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xde,-1, 0, 0, 0, 0, + 0, 0xe7,0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, 0xfe,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0xea,0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1 +#elif (ISO_8859 == 10) + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xc1,-1, 0, 0xc9,0, 0, 0xcd,0, 0, + -1, -1, 0xd3,-1, -1, 0, 0xda,0, 0xdd,-1, + 0xe1,-1, 0, 0xe9,0, 0, 0xed,0, 0, + -1, -1, 0xf3,-1, -1, 0, 0xfa,0, 0xfd,-1, + 0xc2,-1, 0, -1, -1, -1, 0xce,-1, 0, + 0, 0, 0xd4,0, -1, 0, 0xdb,-1, -1, 0, + 0xe2,-1, 0, -1, -1, -1, 0xee,-1, 0, + 0, 0, 0xf4,0, -1, 0, 0xfb,-1, -1, 0, + 0xc3,0, 0, 0, 0, 0, 0xa5,0, 0, + 0, -1, 0xd5,0, 0, 0, 0xd7,0, 0, 0, + 0xe3,0, 0, 0, 0, 0, 0xb5,0, 0, + 0, -1, 0xf5,0, 0, 0, 0xf7,0, 0, 0, + 0xc0,0, 0, 0xa2,0, 0, 0xa4,0, 0, + 0, 0, 0xd2,0, 0, 0, 0xae,0, 0, 0, + 0xe0,0, 0, 0xb2,0, 0, 0xb4,0, 0, + 0, 0, 0xf2,0, 0, 0, 0xbe,0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, 0xcc,-1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, 0xec,-1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0xc4,0, 0, 0xcb,0, 0, 0xcf,0, 0, + 0, 0, 0xd6,0, 0, 0, 0xdc,0, -1, 0, + 0xe4,0, 0, 0xeb,0, 0, 0xef,0, 0, + 0, 0, 0xf6,0, 0, 0, 0xfc,0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xc5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0xe5,0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, 0, 0xa3,0, 0, 0, 0xa6, + 0xa8,0xd1,0, -1, -1, -1, 0, 0, 0, 0, + 0, -1, 0, 0, 0xb3,0, 0, 0, 0xb6, + 0xb8,0xf1,0, -1, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0xa1,0, 0, 0xca,0, 0, 0xc7,0, 0, + 0, 0, 0, 0, 0, 0, 0xd9,0, 0, 0, + 0xb1,0, 0, 0xea,0, 0, 0xe7,0, 0, + 0, 0, 0, 0, 0, 0, 0xf9,0, 0, 0, + 0, 0xc8,-1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, 0xaa,-1, 0, 0, 0, 0xac, + 0, 0xe8,-1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, 0xba,-1, 0, 0, 0, 0xbc +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, -1, 0, -1, 0, 0, -1, 0, 0, + -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, + -1, -1, 0, -1, 0, 0, -1, 0, 0, + -1, -1, -1, -1, -1, 0, -1, 0, -1, -1, + -1, -1, 0, -1, -1, -1, -1, -1, 0, + 0, 0, -1, 0, -1, 0, -1, -1, -1, 0, + -1, -1, 0, -1, -1, -1, -1, -1, 0, + 0, 0, -1, 0, -1, 0, -1, -1, -1, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, -1, 0, 0, + 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, -1, -1, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, -1, 0, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, -1, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, -1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, 0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, + 0, -1, 0, 0, -1, 0, 0, 0, -1, + -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1, + 0, -1, -1, -1, 0, 0, 0, 0, 0, + -1, -1, 0, -1, -1, -1, 0, 0, 0, -1 +#endif +}; + +/* +--- T.61 characters [0xA0 .. 0xBF] ----------------- +*/ +static Couple trans_t61a_iso8859[32] = { +#if (ISO_8859 == 1) || (ISO_8859 == 9) + {'N','S'}, {0xa1,0}, {0xa2,0}, {0xa3,0}, + {'D','O'}, {0xa5,0}, {'C','u'}, {0xa7,0}, + {0xa4,0}, {'\'','6'},{'"','6'}, {0xab,0}, + {'<','-'}, {'-','!'}, {'-','>'}, {'-','v'}, + {0xb0,0}, {0xb1,0}, {0xb2,0}, {0xb3,0}, + {0xd7,0}, {0xb5,0}, {0xb6,0}, {0xb7,0}, + {0xf7,0}, {'\'','9'},{'"','9'}, {0xbb,0}, + {0xbc,0}, {0xbd,0}, {0xbe,0}, {0xbf,0} +#elif (ISO_8859 == 2) || (ISO_8859 == 4) + {'N','S'}, {'!','I'}, {'C','t'}, {'P','d'}, + {'D','O'}, {'Y','e'}, {'C','u'}, {0xa7,0}, + {0xa4,0}, {'\'','6'},{'"','6'}, {'<','<'}, + {'<','-'}, {'-','!'}, {'-','>'}, {'-','v'}, + {0xb0,0}, {'+','-'}, {'2','S'}, {'3','S'}, + {0xd7,0}, {'M','y'}, {'P','I'}, {'.','M'}, + {0xf7,0}, {'\'','9'},{'"','9'}, {'>','>'}, + {'1','4'}, {'1','2'}, {'3','4'}, {'?','I'}, +#elif (ISO_8859 == 3) + {'N','S'}, {'!','I'}, {'C','t'}, {0xa3,0}, + {'D','O'}, {'Y','e'}, {'C','u'}, {0xa7,0}, + {0xa4,0}, {'\'','6'},{'"','6'}, {'<','<'}, + {'<','-'}, {'-','!'}, {'-','>'}, {'-','v'}, + {0xb0,0}, {'+','-'}, {0xb2,0}, {0xb3,0}, + {0xd7,0}, {0xb5,0}, {'P','I'}, {0xb7,0}, + {0xf7,0}, {'\'','9'},{'"','9'}, {'>','>'}, + {'1','4'}, {0xbd,0}, {'3','4'}, {'?','I'} +#elif (ISO_8859 == 10) + {'N','S'}, {'!','I'}, {'C','t'}, {'P','d'}, + {'D','O'}, {'Y','e'}, {'C','u'}, {0xa7,0}, + {'C','u'}, {'\'','6'},{'"','6'}, {'<','<'}, + {'<','-'}, {'-','!'}, {'-','>'}, {'-','v'}, + {0xb0,0}, {'+','-'}, {'2','S'}, {'3','S'}, + {'*','X'}, {'M','y'}, {'P','I'}, {0xb7,0}, + {'-',':'}, {'\'','9'},{'"','9'}, {'>','>'}, + {'1','4'}, {'1','2'}, {'3','4'}, {'?','I'} +#else + {'N','S'}, {'!','I'}, {'C','t'}, {'P','d'}, + {'D','O'}, {'Y','e'}, {'C','u'}, {'S','E'}, + {'X','O'}, {'\'','6'},{'"','6'}, {'<','<'}, + {'<','-'}, {'-','!'}, {'-','>'}, {'-','v'}, + {'D','G'}, {'+','-'}, {'2','S'}, {'3','S'}, + {'*','X'}, {'M','y'}, {'P','I'}, {'.','M'}, + {'-',':'}, {'\'','9'},{'"','9'}, {'>','>'}, + {'1','4'}, {'1','2'}, {'3','4'}, {'?','I'} +#endif +}; + +/* +--- T.61 characters [0xE0 .. 0xFF] ----------------- +*/ +static Couple trans_t61b_iso8859[48] = { +#if (ISO_8859 == 1) + {'-','M'}, {0xb9,0}, {0xae,0}, {0xa9,0}, + {'T','M'}, {'M','8'}, {0xac,0}, {0xa6,0}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {0xc6,0}, {0xd0,0}, {0xaa,0}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {0xd8,0}, {'O','E'}, {0xba,0}, + {0xde,0}, {'T','/'}, {'N','G'}, {'\'','n'}, + {'k','k'}, {0xe6,0}, {'d','/'}, {0xf0,0}, + {'h','/'}, {'i','.'}, {'i','j'}, {'l','.'}, + {'l','/'}, {0xf8,0}, {'o','e'}, {0xdf,0}, + {0xfe,0}, {'t','/'}, {'n','g'}, {'-','-'} +#elif (ISO_8859 == 2) + {'-','M'}, {'1','S'}, {'R','g'}, {'C','o'}, + {'T','M'}, {'M','8'}, {'N','O'}, {'B','B'}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {'A','E'}, {0xd0,0}, {'-','a'}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {0xa3,0}, {'O','/'}, {'O','E'}, {'-','o'}, + {'T','H'}, {'T','/'}, {'N','G'}, {'\'','n'}, + {'k','k'}, {'a','e'}, {0xf0,0}, {'d','-'}, + {'h','/'}, {'i','.'}, {'i','j'}, {'l','.'}, + {0xb3,0}, {'o','/'}, {'o','e'}, {0xdf,0}, + {'t','h'}, {'t','/'}, {'n','g'}, {'-','-'} +#elif (ISO_8859 == 3) + {'-','M'}, {'1','S'}, {'R','g'}, {'C','o'}, + {'T','M'}, {'M','8'}, {'N','O'}, {'B','B'}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {'A','E'}, {'D','/'}, {'-','a'}, + {0xa1,0}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {'O','/'}, {'O','E'}, {'-','o'}, + {'T','H'}, {'T','/'}, {'N','G'}, {'\'','n'}, + {'k','k'}, {'a','e'}, {'d','/'}, {'d','-'}, + {0xb1,0}, {0xb9,0}, {'i','j'}, {'l','.'}, + {'l','/'}, {'o','/'}, {'o','e'}, {0xdf,0}, + {'t','h'}, {'t','/'}, {'n','g'}, {'-','-'} +#elif (ISO_8859 == 4) + {'-','M'}, {'1','S'}, {'R','g'}, {'C','o'}, + {'T','M'}, {'M','8'}, {'N','O'}, {'B','B'}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {0xc6,0}, {0xd0,0}, {'-','a'}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {0xd8,0}, {'O','E'}, {'-','o'}, + {'T','H'}, {0xac,0}, {0xbd,0}, {'\'','n'}, + {0xa2,0}, {0xe6,0}, {0xf0,0}, {'d','-'}, + {'h','/'}, {'i','.'}, {'i','j'}, {'l','.'}, + {'l','/'}, {0xf8,0}, {'o','e'}, {0xdf,0}, + {'t','h'}, {0xbc,0}, {0xbf,0}, {'-','-'} +#elif (ISO_8859 == 9) + {'-','M'}, {0xb9,0}, {0xae,0}, {0xa9,0}, + {'T','M'}, {'M','8'}, {0xac,0}, {0xa6,0}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {0xc6,0}, {'D','/'}, {0xaa,0}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {0xd8,0}, {'O','E'}, {0xba,0}, + {'T','H'}, {'T','/'}, {'N','G'}, {'\'','n'}, + {'k','k'}, {0xe6,0}, {'d','/'}, {'d','-'}, + {'h','/'}, {0xfd,0}, {'i','j'}, {'l','.'}, + {'l','/'}, {0xf8,0}, {'o','e'}, {0xdf,0}, + {'t','h'}, {'t','/'}, {'n','g'}, {'-','-'} +#elif (ISO_8859 == 10) + {0xbd,0}, {'1','S'}, {'R','g'}, {'C','o'}, + {'T','M'}, {'M','8'}, {'N','O'}, {'B','B'}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {0xc6,0}, {0xa9,0}, {'-','a'}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {0xd8,0}, {'O','E'}, {'-','o'}, + {0xde,0}, {0xab,0}, {0xaf,0}, {'\'','n'}, + {0xff,0}, {0xe6,0}, {0xb9,0}, {0xf0,0}, + {'h','/'}, {'i','.'}, {'i','j'}, {'l','.'}, + {'l','/'}, {0xf8,0}, {'o','e'}, {0xdf,0}, + {0xfe,0}, {0xbb,0}, {0xbf,0}, {'-','-'} +#else + {'-','M'}, {'1','S'}, {'R','g'}, {'C','o'}, + {'T','M'}, {'M','8'}, {'N','O'}, {'B','B'}, + {0,0}, {0,0}, {0,0}, {0,0}, + {'1','8'}, {'3','8'}, {'5','8'}, {'7','8'}, + {'O','m'}, {'A','E'}, {'D','/'}, {'-','a'}, + {'H','/'}, {0,0}, {'I','J'}, {'L','.'}, + {'L','/'}, {'O','/'}, {'O','E'}, {'-','o'}, + {'T','H'}, {'T','/'}, {'N','G'}, {'\'','n'}, + {'k','k'}, {'a','e'}, {'d','/'}, {'d','-'}, + {'h','/'}, {'i','.'}, {'i','j'}, {'l','.'}, + {'l','/'}, {'o','/'}, {'o','e'}, {'s','s'}, + {'t','h'}, {'t','-'}, {'n','g'}, {'-','-'} +#endif +}; + +/* +--- ISO 8859-n characters <0xA0 .. 0xFF> ------------------- +*/ +#if (ISO_8859 == 1) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xa1,0}, {0xa2,0}, {0xa3,0}, + {0xa8,0}, {0xa5,0}, {0xd7,0}, {0xa7,0}, + {0xc8,ALONE}, {0xd3,0}, {0xe3,0}, {0xab,0}, + {0xd6,0}, {0xff,0}, {0xd2,0}, {0xc5,ALONE}, + {0xb0,0}, {0xb1,0}, {0xb2,0}, {0xb3,0}, + {0xc2,ALONE}, {0xb5,0}, {0xb6,0}, {0xb7,0}, + {0xcb,ALONE}, {0xd1,0}, {0xeb,0}, {0xbb,0}, + {0xbc,0}, {0xbd,0}, {0xbe,0}, {0xbf,0}, + {0xc1,'A'}, {0xc2,'A'}, {0xc3,'A'}, {0xc4,'A'}, + {0xc8,'A'}, {0xca,'A'}, {0xe1,0}, {0xcb,'C'}, + {0xc1,'E'}, {0xc2,'E'}, {0xc3,'E'}, {0xc8,'E'}, + {0xc1,'I'}, {0xc2,'I'}, {0xc3,'I'}, {0xc8,'I'}, + {0xe2,0}, {0xc4,'N'}, {0xc1,'O'}, {0xc2,'O'}, + {0xc3,'O'}, {0xc4,'O'}, {0xc8,'O'}, {0xb4,0}, + {0xe9,0}, {0xc1,'U'}, {0xc2,'U'}, {0xc3,'U'}, + {0xc8,'U'}, {0xc2,'Y'}, {0xec,0}, {0xfb,0}, + {0xc1,'a'}, {0xc2,'a'}, {0xc3,'a'}, {0xc4,'a'}, + {0xc8,'a'}, {0xca,'a'}, {0xf1,0}, {0xcb,'c'}, + {0xc1,'e'}, {0xc2,'e'}, {0xc3,'e'}, {0xc8,'e'}, + {0xc1,'i'}, {0xc2,'i'}, {0xc3,'i'}, {0xc8,'i'}, + {0xf3,0}, {0xc4,'n'}, {0xc1,'o'}, {0xc2,'o'}, + {0xc3,'o'}, {0xc4,'o'}, {0xc8,'o'}, {0xb8,0}, + {0xf9,0}, {0xc1,'u'}, {0xc2,'u'}, {0xc3,'u'}, + {0xc8,'u'}, {0xc2,'y'}, {0xfc,0}, {0xc8,'y'} +}; +#elif (ISO_8859 == 2) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xce,'A'}, {0xc6,ALONE}, {0xe8,0}, + {0xa8,0}, {0xcf,'L'}, {0xc2,'S'}, {0xa7,0}, + {0xc8,ALONE}, {0xcf,'S'}, {0xcb,'S'}, {0xcf,'T'}, + {0xc2,'Z'}, {0xff,0}, {0xcf,'Z'}, {0xc7,'Z'}, + {0xb0,0}, {0xce,'a'}, {0xce,ALONE}, {0xf8,0}, + {0xc2,ALONE}, {0xcf,'l'}, {0xc2,'s'}, {0xcf,ALONE}, + {0xcb,ALONE}, {0xcf,'s'}, {0xcb,'s'}, {0xcf,'t'}, + {0xc2,'z'}, {0xcd,ALONE}, {0xcf,'z'}, {0xc7,'z'}, + {0xc2,'R'}, {0xc2,'A'}, {0xc3,'A'}, {0xc6,'A'}, + {0xc8,'A'}, {0xc2,'L'}, {0xc2,'C'}, {0xcb,'C'}, + {0xcf,'C'}, {0xc2,'E'}, {0xce,'E'}, {0xc8,'E'}, + {0xcf,'E'}, {0xc2,'I'}, {0xc3,'I'}, {0xcf,'D'}, + {0xe2,0}, {0xc2,'N'}, {0xcf,'N'}, {0xc2,'O'}, + {0xc3,'O'}, {0xcd,'O'}, {0xc8,'O'}, {0xb4,0}, + {0xcf,'R'}, {0xca,'U'}, {0xc2,'U'}, {0xcd,'U'}, + {0xc8,'U'}, {0xc2,'Y'}, {0xcb,'T'}, {0xfb,0}, + {0xc2,'r'}, {0xc2,'a'}, {0xc3,'a'}, {0xc6,'a'}, + {0xc8,'a'}, {0xc2,'l'}, {0xc2,'c'}, {0xcb,'c'}, + {0xcf,'c'}, {0xc2,'e'}, {0xce,'e'}, {0xc8,'e'}, + {0xcf,'e'}, {0xc2,'i'}, {0xc3,'i'}, {0xcf,'d'}, + {0xf2,0}, {0xc2,'n'}, {0xcf,'n'}, {0xc2,'o'}, + {0xc3,'o'}, {0xcd,'o'}, {0xc8,'o'}, {0xb8,0}, + {0xcf,'r'}, {0xca,'u'}, {0xc2,'u'}, {0xcd,'u'}, + {0xc8,'u'}, {0xc2,'y'}, {0xcb,'t'}, {0xc7,ALONE} +}; +#elif (ISO_8859 == 3) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xe4,0}, {0xc6,ALONE}, {0xa3,0}, + {0xa8,0}, {0,0}, {0xc3,'H'}, {0xa7,0}, + {0xc8,ALONE}, {0xc7,'I'}, {0xcb,'S'}, {0xc6,'G'}, + {0xc3,'J'}, {0xff,0}, {0,0}, {0xc7,'Z'}, + {0xb0,0}, {0xf4,0}, {0xb2,0}, {0xb3,0}, + {0xc2,ALONE}, {0xb5,0}, {0xc3,'h'}, {0xb7,0}, + {0xcb,ALONE}, {0xf5,0}, {0xcb,'s'}, {0xc6,'g'}, + {0xc3,'j'}, {0xbd,0}, {0,0}, {0xc7,'z'}, + {0xc1,'A'}, {0xc2,'A'}, {0xc3,'A'}, {0,0}, + {0xc8,'A'}, {0xc7,'C'}, {0xc3,'C'}, {0xcb,'C'}, + {0xc1,'E'}, {0xc2,'E'}, {0xc3,'E'}, {0xc8,'E'}, + {0xc1,'I'}, {0xc2,'I'}, {0xc3,'I'}, {0xc8,'I'}, + {0,0}, {0xc4,'N'}, {0xc1,'O'}, {0xc2,'O'}, + {0xc3,'O'}, {0xc7,'G'}, {0xc8,'O'}, {0xb4,0}, + {0xc3,'G'}, {0xc1,'U'}, {0xc2,'U'}, {0xc3,'U'}, + {0xc8,'U'}, {0xc6,'U'}, {0xc3,'S'}, {0xfb,0}, + {0xc1,'a'}, {0xc2,'a'}, {0xc3,'a'}, {0,0}, + {0xc8,'a'}, {0xc7,'c'}, {0xc3,'c'}, {0xcb,'c'}, + {0xc1,'e'}, {0xc2,'e'}, {0xc3,'e'}, {0xc8,'e'}, + {0xc1,'i'}, {0xc2,'i'}, {0xc3,'i'}, {0xc8,'i'}, + {0,0}, {0xc4,'n'}, {0xc1,'o'}, {0xc2,'o'}, + {0xc3,'o'}, {0xc7,'g'}, {0xc8,'o'}, {0xb8,0}, + {0xc3,'g'}, {0xc1,'u'}, {0xc2,'u'}, {0xc3,'u'}, + {0xc8,'u'}, {0xc6,'u'}, {0xc3,'s'}, {0xc7,ALONE} +}; +#elif (ISO_8859 == 4) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xce,'A'}, {0xf0,0}, {0xcb,'R'}, + {0xa8,0}, {0xc4,'I'}, {0xcb,'L'}, {0xa7,0}, + {0xc8,ALONE}, {0xcf,'S'}, {0xc5,'E'}, {0xcb,'G'}, + {0xed,0}, {0xff,0}, {0xcf,'Z'}, {0xc5,ALONE}, + {0xb0,0}, {0xce,'a'}, {0xce,ALONE}, {0xcb,'r'}, + {0xc2,ALONE}, {0xc4,'i'}, {0xcb,'l'}, {0xcf,ALONE}, + {0xcb,ALONE}, {0xcf,'s'}, {0xc5,'e'}, {0xcb,'g'}, + {0xfd,0}, {0xee,0}, {0xcf,'z'}, {0xfe,0}, + {0xc5,'A'}, {0xc2,'A'}, {0xc3,'A'}, {0xc4,'A'}, + {0xc8,'A'}, {0xca,'A'}, {0xe1,0}, {0xce,'I'}, + {0xcf,'C'}, {0xc2,'E'}, {0xce,'E'}, {0xc8,'E'}, + {0xc7,'E'}, {0xc2,'I'}, {0xc3,'I'}, {0xc5,'I'}, + {0xe2,0}, {0xcb,'N'}, {0xc5,'O'}, {0xcb,'K'}, + {0xc3,'O'}, {0xc4,'O'}, {0xc8,'O'}, {0xb4,0}, + {0xe9,0}, {0xce,'U'}, {0xc2,'U'}, {0xc3,'U'}, + {0xc8,'U'}, {0xc4,'U'}, {0xc5,'U'}, {0xfb,0}, + {0xc5,'a'}, {0xc2,'a'}, {0xc3,'a'}, {0xc4,'a'}, + {0xc8,'a'}, {0xca,'a'}, {0xf1,0}, {0xce,'i'}, + {0xcf,'c'}, {0xc2,'e'}, {0xce,'e'}, {0xc8,'e'}, + {0xc7,'e'}, {0xc2,'i'}, {0xc3,'i'}, {0xc5,'i'}, + {0xf2,0}, {0xcb,'n'}, {0xc5,'o'}, {0xcb,'k'}, + {0xc3,'o'}, {0xc4,'o'}, {0xc8,'o'}, {0xb8,0}, + {0xf9,0}, {0xce,'u'}, {0xc2,'u'}, {0xc3,'u'}, + {0xc8,'u'}, {0xc4,'u'}, {0xc5,'u'}, {0xc7,ALONE} +}; +#elif (ISO_8859 == 9) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xa1,0}, {0xa2,0}, {0xa3,0}, + {0xa8,0}, {0xa5,0}, {0xd7,0}, {0xa7,0}, + {0xc8,ALONE}, {0xd3,0}, {0xe3,0}, {0xab,0}, + {0xd6,0}, {0xff,0}, {0xd2,0}, {0xc5,ALONE}, + {0xb0,0}, {0xb1,0}, {0xb2,0}, {0xb3,0}, + {0xc2,ALONE}, {0xb5,0}, {0xb6,0}, {0xb7,0}, + {0xcb,ALONE}, {0xd1,0}, {0xeb,0}, {0xbb,0}, + {0xbc,0}, {0xbd,0}, {0xbe,0}, {0xbf,0}, + {0xc1,'A'}, {0xc2,'A'}, {0xc3,'A'}, {0xc4,'A'}, + {0xc8,'A'}, {0xca,'A'}, {0xe1,0}, {0xcb,'C'}, + {0xc1,'E'}, {0xc2,'E'}, {0xc3,'E'}, {0xc8,'E'}, + {0xc1,'I'}, {0xc2,'I'}, {0xc3,'I'}, {0xc8,'I'}, + {0xc6,'G'}, {0xc4,'N'}, {0xc1,'O'}, {0xc2,'O'}, + {0xc3,'O'}, {0xc4,'O'}, {0xc8,'O'}, {0xb4,0}, + {0xe9,0}, {0xc1,'U'}, {0xc2,'U'}, {0xc3,'U'}, + {0xc8,'U'}, {0xc7,'I'}, {0xcb,'S'}, {0xfb,0}, + {0xc1,'a'}, {0xc2,'a'}, {0xc3,'a'}, {0xc4,'a'}, + {0xc8,'a'}, {0xca,'a'}, {0xf1,0}, {0xcb,'c'}, + {0xc1,'e'}, {0xc2,'e'}, {0xce,'e'}, {0xc8,'e'}, + {0xc7,'e'}, {0xc2,'i'}, {0xc3,'i'}, {0xc5,'i'}, + {0xc6,'g'}, {0xc4,'n'}, {0xc1,'o'}, {0xc2,'o'}, + {0xc3,'o'}, {0xc4,'o'}, {0xc8,'o'}, {0xb8,0}, + {0xf9,0}, {0xc1,'u'}, {0xc2,'u'}, {0xc3,'u'}, + {0xc8,'u'}, {0xf5,0}, {0xcb,'s'}, {0xc8,'y'} +}; +#elif (ISO_8859 == 10) +static Couple trans_iso8859_t61[96] = { + {0xa0,0}, {0xce,'A'}, {0xc5,'E'}, {0xcb,'G'}, + {0xc5,'I'}, {0xc4,'I'}, {0xcb,'K'}, {0xa7,0}, + {0xcb,'L'}, {0xe2,0}, {0xcf,'S'}, {0xed,0}, + {0xcf,'Z'}, {0xff,0}, {0xc5,'U'}, {0xee,0}, + {0xb0,0}, {0xce,'a'}, {0xc5,'e'}, {0xcb,'g'}, + {0xc5,'i'}, {0xc4,'i'}, {0xcb,'k'}, {0xb7,0}, + {0xcb,'l'}, {0xf2,0}, {0xcf,'s'}, {0xfd,0}, + {0xcf,'z'}, {0xd0,0}, {0xc5,'u'}, {0xfe,0}, + {0xc5,'A'}, {0xc2,'A'}, {0xc3,'A'}, {0xc4,'A'}, + {0xc8,'A'}, {0xca,'A'}, {0xe1,0}, {0xce,'I'}, + {0xcf,'C'}, {0xc2,'E'}, {0xce,'E'}, {0xc8,'E'}, + {0xc7,'E'}, {0xc2,'I'}, {0xc3,'I'}, {0xc8,'I'}, + {0,0}, {0xcb,'N'}, {0xc5,'O'}, {0xc2,'O'}, + {0xc3,'O'}, {0xc4,'O'}, {0xc8,'O'}, {0xc4,'U'}, + {0xe9,0}, {0xce,'U'}, {0xc2,'U'}, {0xc3,'U'}, + {0xc8,'U'}, {0xc2,'Y'}, {0xec,0}, {0xfb,0}, + {0xc5,'a'}, {0xc2,'a'}, {0xc3,'a'}, {0xc4,'a'}, + {0xc8,'a'}, {0xca,'a'}, {0xf1,0}, {0xce,'i'}, + {0xcf,'c'}, {0xc2,'e'}, {0xce,'e'}, {0xc8,'e'}, + {0xc7,'e'}, {0xc2,'i'}, {0xc3,'i'}, {0xc8,'i'}, + {0xf3,0}, {0xcb,'n'}, {0xc5,'o'}, {0xc2,'o'}, + {0xc3,'o'}, {0xc4,'o'}, {0xc8,'o'}, {0xc4,'u'}, + {0xf9,0}, {0xce,'u'}, {0xc2,'u'}, {0xc3,'u'}, + {0xc8,'u'}, {0xc2,'y'}, {0xfc,0}, {0xf0,0} +}; +#endif + + +static Byte * +c_to_hh( Byte *o, Byte c ) +{ + Byte n; + + *o++ = '{'; *o++ = 'x'; + n = c >> 4; + *o++ = ((n < 0xA) ? '0' : 'A' - 0xA) + n; + n = c & 0x0F; + *o++ = ((n < 0xA) ? '0' : 'A' - 0xA) + n; + *o++ = '}'; + return o; +} + + +static Byte * +c_to_cc( Byte *o, Couple *cc, Byte c ) +{ + if ( (*cc).a != 0 ) { + if ( (*cc).b == 0 ) + *o++ = (*cc).a; + else { + *o++ = '{'; + *o++ = (*cc).a; + *o++ = (*cc).b; + *o++ = '}'; + } + return o; + } + else + return c_to_hh( o, c ); +} + +/* --- routine to convert from T.61 to ISO 8859-n --- */ + +int +ldap_t61_to_8859( char **bufp, unsigned long *buflenp, int free_input ) +{ + Byte *s, *oo, *o; + unsigned int n; + int c; + unsigned long len; + Couple *cc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_t61_to_8859 input length: %ld\n", + *buflenp, 0, 0 ); + + len = *buflenp; + s = (Byte *) *bufp; + + if ( (o = oo = (Byte *)NSLDAPI_MALLOC( 2 * len + 64 )) == NULL ) { + return( 1 ); + } + + while ( (char *)s - *(char **)bufp < len ) { + switch ( *s >> 4 ) { + + case 0xA: case 0xB: + o = c_to_cc( o, &trans_t61a_iso8859[ *s - 0xA0 ], *s ); + s++; + break; + + case 0xD: case 0xE: case 0xF: + o = c_to_cc( o, &trans_t61b_iso8859[ *s - 0xD0 ], *s ); + s++; + break; + + case 0xC: + if ( (*s == 0xC0) || (*s == 0xC9) || (*s == 0xCC) ) { + o = c_to_hh( o, *s++ ); + break; + } + + n = (*s++) - 0xC0; + switch ( *s ) { + + case 'A': c = letter_w_diacritic[n][0]; break; + case 'C': c = letter_w_diacritic[n][1]; break; + case 'D': c = letter_w_diacritic[n][2]; break; + case 'E': c = letter_w_diacritic[n][3]; break; + case 'G': c = letter_w_diacritic[n][4]; break; + case 'H': c = letter_w_diacritic[n][5]; break; + case 'I': c = letter_w_diacritic[n][6]; break; + case 'J': c = letter_w_diacritic[n][7]; break; + case 'K': c = letter_w_diacritic[n][8]; break; + case 'L': c = letter_w_diacritic[n][9]; break; + case 'N': c = letter_w_diacritic[n][10]; break; + case 'O': c = letter_w_diacritic[n][11]; break; + case 'R': c = letter_w_diacritic[n][12]; break; + case 'S': c = letter_w_diacritic[n][13]; break; + case 'T': c = letter_w_diacritic[n][14]; break; + case 'U': c = letter_w_diacritic[n][15]; break; + case 'W': c = letter_w_diacritic[n][16]; break; + case 'Y': c = letter_w_diacritic[n][17]; break; + case 'Z': c = letter_w_diacritic[n][18]; break; + + case 'a': c = letter_w_diacritic[n][19]; break; + case 'c': c = letter_w_diacritic[n][20]; break; + case 'd': c = letter_w_diacritic[n][21]; break; + case 'e': c = letter_w_diacritic[n][22]; break; + case 'g': c = letter_w_diacritic[n][23]; break; + case 'h': c = letter_w_diacritic[n][24]; break; + case 'i': c = letter_w_diacritic[n][25]; break; + case 'j': c = letter_w_diacritic[n][26]; break; + case 'k': c = letter_w_diacritic[n][27]; break; + case 'l': c = letter_w_diacritic[n][28]; break; + case 'n': c = letter_w_diacritic[n][29]; break; + case 'o': c = letter_w_diacritic[n][30]; break; + case 'r': c = letter_w_diacritic[n][31]; break; + case 's': c = letter_w_diacritic[n][32]; break; + case 't': c = letter_w_diacritic[n][33]; break; + case 'u': c = letter_w_diacritic[n][34]; break; + case 'w': c = letter_w_diacritic[n][35]; break; + case 'y': c = letter_w_diacritic[n][36]; break; + case 'z': c = letter_w_diacritic[n][37]; break; + + case ALONE: c = (( !diacritic[n].b ) ? diacritic[n].a : -1); + break; + + default: c = 0; + } + + if ( c > 0 ) { + *o++ = c; s++; + } else { + *o++ = '{'; + if ( c == -1 ) { + *o++ = ( ( *s == ALONE ) ? ' ' : *s ); + s++; + } else { + *o++ = '"'; + } + *o++ = diacritic[n].a; + *o++ = '}'; + } + break; + +#if (ISO_8859 == 0) + case 0x8: case 0x9: + *o++ = 0x1B; /* */ + *o++ = *s++ - 0x40; + break; +#endif + + default: + *o++ = *s++; + } + } + + len = o - oo; + o = oo; + + if ( (oo = (Byte *)NSLDAPI_REALLOC( o, len )) == NULL ) { + NSLDAPI_FREE( o ); + return( 1 ); + } + + if ( free_input ) { + NSLDAPI_FREE( *bufp ); + } + *bufp = (char *) oo; + *buflenp = len; + return( 0 ); +} + + +static int +hh_to_c( Byte *h ) +{ + Byte c; + + if ( (*h >= '0') && (*h <= '9') ) c = *h++ - '0'; + else if ( (*h >= 'A') && (*h <= 'F') ) c = *h++ - 'A' + 10; + else if ( (*h >= 'a') && (*h <= 'f') ) c = *h++ - 'a' + 10; + else return -1; + + c <<= 4; + + if ( (*h >= '0') && (*h <= '9') ) c |= *h - '0'; + else if ( (*h >= 'A') && (*h <= 'F') ) c |= *h - 'A' + 10; + else if ( (*h >= 'a') && (*h <= 'f') ) c |= *h - 'a' + 10; + else return -1; + + return c; +} + + +static Byte * +cc_to_t61( Byte *o, Byte *s ) +{ + int n, c = 0; + + switch ( *(s + 1) ) { + + case '`': c = -1; break; /* */ + + case '!': + switch ( *s ) { + case '!': c = 0x7C; break; /* */ + case '(': c = 0x7B; break; /* */ + case '-': c = 0xAD; break; /* */ + default: c = -1; /* */ + } + break; + +#if (ISO_8859 == 1) || (ISO_8859 == 2) || (ISO_8859 == 3) || \ + (ISO_8859 == 4) || (ISO_8859 == 9) + case 0xB4: +#endif + case '\'': c = -2; break; /* */ + + case '^': c = -3; break; /* */ + + case '>': + switch ( *s ) { + case ')': c = 0x5D; break; /* */ + case '>': c = 0xBB; break; /* */ + case '-': c = 0xAE; break; /* */ + default: c = -3; /* */ + } + break; + + case '~': + case '?': c = -4; break; /* */ + +#if (ISO_8859 == 1) || (ISO_8859 == 4) || (ISO_8859 == 9) + case 0xAF: c = -5; break; /* */ +#endif + + case '-': + switch ( *s ) { + case '-': c = 0xFF; break; /* */ + case '<': c = 0xAC; break; /* */ + case '+': c = 0xB1; break; /* */ + case 'd': c = 0xF3; break; /* */ + default: c = -5; /* */ + } + break; + +#if (ISO_8859 == 2) || (ISO_8859 == 3) + case 0xA2: c = -6; break; /* */ +#endif + + case '(': + if ( *s == '<' ) c = 0x5B; /* */ + else c = -6; /* */ + break; + +#if (ISO_8859 == 2) || (ISO_8859 == 3) || (ISO_8859 == 4) + case 0xFF: c = -7; break; /* */ +#endif + + case '.': + switch ( *s ) { + case 'i': c = 0xF5; break; /* */ + case 'L': c = 0xE7; break; /* */ + case 'l': c = 0xF7; break; /* */ + default: c = -7; /* */ + } + break; + +#if (ISO_8859 == 1) || (ISO_8859 == 2) || (ISO_8859 == 3) || \ + (ISO_8859 == 4) || (ISO_8859 == 9) + case 0xA8: c = -8; break; /* */ +#endif + + case ':': + if ( *s == '-') c = 0xB8; /* */ + else c = -8; /* */ + break; + +#if (ISO_8859 == 1) || (ISO_8859 == 2) || (ISO_8859 == 3) || \ + (ISO_8859 == 4) || (ISO_8859 == 9) || (ISO_8859 == 10) + case 0xB0: +#endif + case '0': c = -10; break; /* */ + +#if (ISO_8859 == 1) || (ISO_8859 == 2) || (ISO_8859 == 3) || \ + (ISO_8859 == 4) || (ISO_8859 == 9) + case 0xB8: +#endif + case ',': c = -11; break; /* */ + +#if (ISO_8859 == 2) + case 0xBD: +#endif + case '"': c = -13; break; /* */ + +#if (ISO_8859 == 2) || (ISO_8859 == 4) + case 0xB2: +#endif + case ';': c = -14; break; /* */ + +#if (ISO_8859 == 2) || (ISO_8859 == 4) + case 0xB7: c = -15; break; /* */ +#endif + + case ')': + if ( *s == '!' ) c = 0x7D; /* */ + break; + + case '<': + if ( *s == '<' ) c = 0xAB; /* */ + else c = -15; /* */ + break; + + case '/': + switch ( *s ) { + case '/': c = 0x5C; break; /* */ + case 'D': c = 0xE2; break; /* */ + case 'd': c = 0xF2; break; /* */ + case 'H': c = 0xE4; break; /* */ + case 'h': c = 0xF4; break; /* */ + case 'L': c = 0xE8; break; /* */ + case 'l': c = 0xF8; break; /* */ + case 'O': c = 0xE9; break; /* */ + case 'o': c = 0xF9; break; /* */ + case 'T': c = 0xED; break; /* */ + case 't': c = 0xFD; break; /* */ + } + break; + + case '2': + if ( *s == '1' ) c = 0xBD; /* */ + break; + + case '4': + switch ( *s ) { + case '1': c = 0xBC; break; /* */ + case '3': c = 0xBE; break; /* */ + } + break; + + case '6': + switch ( *s ) { + case '\'': c = 0xA9; break; /* */ + case '"': c = 0xAA; break; /* */ + } + break; + + case '8': + switch ( *s ) { + case '1': c = 0xDC; break; /* */ + case '3': c = 0xDD; break; /* */ + case '5': c = 0xDE; break; /* */ + case '7': c = 0xDF; break; /* */ + case 'M': c = 0xD5; break; /* */ + } + break; + + case '9': + switch ( *s ) { + case '\'': c = 0xB9; break; /* */ + case '"': c = 0xBA; break; /* */ + } + break; + + case 'A': + if ( *s == 'A' ) c = -10; /* + */ + break; + + case 'a': + switch ( *s ) { + case '-': c = 0xE3; break; /* */ + case 'a': c = -10; break; /* + */ + } + break; + + case 'B': + if ( *s == 'B' ) c = 0xD7; /* */ + break; + + case 'b': + if ( *s == 'N' ) c = 0xA6; /* */ + break; + + case 'd': + if ( *s == 'P' ) c = 0xA3; /* */ + break; + + case 'E': + switch ( *s ) { + case 'S': c = 0xA7; break; /* */ + case 'A': c = 0xE1; break; /* */ + case 'O': c = 0xEA; break; /* */ + } + break; + + case 'e': + switch ( *s ) { + case 'a': c = 0xF1; break; /* */ + case 'o': c = 0xFA; break; /* */ + case 'Y': c = 0xA5; break; /* */ + } + break; + + case 'G': + switch ( *s ) { + case 'D': c = 0xB0; break; /* */ + case 'N': c = 0xEE; break; /* */ + } + break; + + case 'g': + switch ( *s ) { + case 'R': c = 0xD2; break; /* */ + case 'n': c = 0xFE; break; /* */ + } + break; + + case 'H': + if ( *s == 'T' ) c = 0xEC; /* */ + break; + + case 'h': + if ( *s == 't' ) c = 0xFC; /* */ + break; + + case 'I': + switch ( *s ) { + case 'P': c = 0xB6; break; /* */ + case '!': c = 0xA1; break; /* */ + case '?': c = 0xBF; break; /* */ + } + break; + + case 'J': + if ( *s == 'I' ) c = 0xE6; /* */ + break; + + case 'j': + if ( *s == 'i' ) c = 0xF6; /* */ + break; + + case 'k': + if ( *s == 'k' ) c = 0xF0; /* */ + break; + + case 'M': + switch ( *s ) { + case '.': c = 0xB7; break; /* */ + case '-': c = 0xD0; break; /* */ + case 'T': c = 0xD4; break; /* */ + } + break; + + case 'm': + switch ( *s ) { + case '\'': /* RFC 1345 */ + case ' ': c = -5; break; /* */ + case 'O': c = 0xE0; break; /* */ + } + break; + + case 'n': + if ( *s == '\'' ) c = 0xEF; /* */ + break; + + case 'O': + switch ( *s ) { + case 'D': c = 0xA4; break; /* */ + case 'N': c = 0xD6; break; /* */ + } + break; + + case 'o': + switch ( *s ) { + case 'C': c = 0xD3; break; /* */ + case '-': c = 0xEB; break; /* */ + } + break; + + case 'S': + switch ( *s ) { + case '1': c = 0xD1; break; /* */ + case '2': c = 0xB2; break; /* */ + case '3': c = 0xB3; break; /* */ + case 'N': c = 0xA0; break; /* */ + } + break; + + case 's': + if ( *s == 's' ) c = 0xFB; /* */ + break; + + case 't': + if ( *s == 'C' ) c = 0xA2; /* */ + break; + + case 'u': + if ( *s == 'C' ) c = 0xA8; /* */ + break; + + case 'v': + if ( *s == '-' ) c = 0xAF; /* */ + break; + + case 'X': + if ( *s == '*' ) c = 0xB4; /* */ + break; + + case 'y': + if ( *s == 'M' ) c = 0xB5; /* */ + break; + } + + if ( c > 0 ) { + *o++ = c; + return o; + } else if ( !c ) + return NULL; + + /* else: c < 0 */ + n = -c; + switch ( *s ) { + + case 'A': c = letter_w_diacritic[n][0]; break; + case 'C': c = letter_w_diacritic[n][1]; break; + case 'D': c = letter_w_diacritic[n][2]; break; + case 'E': c = letter_w_diacritic[n][3]; break; + case 'G': c = letter_w_diacritic[n][4]; break; + case 'H': c = letter_w_diacritic[n][5]; break; + case 'I': c = letter_w_diacritic[n][6]; break; + case 'J': c = letter_w_diacritic[n][7]; break; + case 'K': c = letter_w_diacritic[n][8]; break; + case 'L': c = letter_w_diacritic[n][9]; break; + case 'N': c = letter_w_diacritic[n][10]; break; + case 'O': c = letter_w_diacritic[n][11]; break; + case 'R': c = letter_w_diacritic[n][12]; break; + case 'S': c = letter_w_diacritic[n][13]; break; + case 'T': c = letter_w_diacritic[n][14]; break; + case 'U': c = letter_w_diacritic[n][15]; break; + case 'W': c = letter_w_diacritic[n][16]; break; + case 'Y': c = letter_w_diacritic[n][17]; break; + case 'Z': c = letter_w_diacritic[n][18]; break; + + case 'a': c = letter_w_diacritic[n][19]; break; + case 'c': c = letter_w_diacritic[n][20]; break; + case 'd': c = letter_w_diacritic[n][21]; break; + case 'e': c = letter_w_diacritic[n][22]; break; + case 'g': c = letter_w_diacritic[n][23]; break; + case 'h': c = letter_w_diacritic[n][24]; break; + case 'i': c = letter_w_diacritic[n][25]; break; + case 'j': c = letter_w_diacritic[n][26]; break; + case 'k': c = letter_w_diacritic[n][27]; break; + case 'l': c = letter_w_diacritic[n][28]; break; + case 'n': c = letter_w_diacritic[n][29]; break; + case 'o': c = letter_w_diacritic[n][30]; break; + case 'r': c = letter_w_diacritic[n][31]; break; + case 's': c = letter_w_diacritic[n][32]; break; + case 't': c = letter_w_diacritic[n][33]; break; + case 'u': c = letter_w_diacritic[n][34]; break; + case 'w': c = letter_w_diacritic[n][35]; break; + case 'y': c = letter_w_diacritic[n][36]; break; + case 'z': c = letter_w_diacritic[n][37]; break; + + case '\'': + case ' ': c = -1; break; + + default: c = 0; + } + + if ( !c ) + return NULL; + + *o++ = n + 0xC0; + *o++ = ( ( (*s == ' ') || (*s == '\'') ) ? ALONE : *s ); + return o; +} + + +/* --- routine to convert from ISO 8859-n to T.61 --- */ + +int +ldap_8859_to_t61( char **bufp, unsigned long *buflenp, int free_input ) +{ + Byte *s, *oo, *o, *aux; + int c; + unsigned long len; + Couple *cc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_8859_to_t61 input length: %ld\n", + *buflenp, 0, 0 ); + + len = *buflenp; + s = (Byte *) *bufp; + + if ( (o = oo = (Byte *)NSLDAPI_MALLOC( 2 * len + 64 )) == NULL ) { + return( 1 ); + } + + while ( (char *)s - *(char **)bufp < len ) { + switch( *s >> 5 ) { + + case 2: + switch ( *s ) { + + case '^': *o++ = 0xC3; *o++ = ALONE; s++; break; + + case '\\': + s++; + if ( (c = hh_to_c( s )) != -1 ) { + *o++ = c; + s += 2; + } else + *o++ = '\\'; + break; + + default: *o++ = *s++; + } + break; + + case 3: + switch ( *s ) { + + case '`': *o++ = 0xC1; *o++ = ALONE; s++; break; + case '~': *o++ = 0xC4; *o++ = ALONE; s++; break; + + case '{': + s++; + if ( *(s + 2) == '}' ) { + if ( (aux = cc_to_t61( o, s )) != NULL ) { + o = aux; + s += 3; + } else { + *o++ = '{'; + } + } else if ( (*(s + 3) == '}') && ( (*s == 'x') || (*s == 'X') ) && + ( (c = hh_to_c( s + 1 )) != -1 ) ) { + *o++ = c; + s += 4; + } else { + *o++ = '{'; + } + break; + + default: + *o++ = *s++; + } + break; + +#if (ISO_8859 == 0) + case 4: case 5: case 6: case 7: + s++; + break; +#else + case 5: case 6: case 7: +# if (ISO_8859 == 1) || (ISO_8859 == 2) || (ISO_8859 == 3) || \ + (ISO_8859 == 4) || (ISO_8859 == 9) || (ISO_8859 == 10) + if ( (*(cc = &trans_iso8859_t61[ *s - 0xA0 ])).a ) { + *o++ = (*cc).a; + if ( (*cc).b ) *o++ = (*cc).b; + } +# endif + s++; + break; +#endif + + default: + *o++ = *s++; + } + } + + len = o - oo; + o = oo; + + if ( (oo = (Byte *)NSLDAPI_REALLOC( o, len )) == NULL ) { + NSLDAPI_FREE( o ); + return( 1 ); + } + + if ( free_input ) { + NSLDAPI_FREE( *bufp ); + } + *bufp = (char *) oo; + *buflenp = len; + return( 0 ); +} + + +#ifdef NOT_NEEDED_IN_LIBLDAP /* mcs@umich.edu 12 Oct 1995 */ +/* --- routine to convert "escaped" (\hh) characters to 8bits --- */ + +void convert_escaped_to_8bit( s ) +char *s; +{ + char *o = s; + int c; + + while ( *s ) { + if ( *s == '\\' ) { + if ( (c = hh_to_c( ++s )) != -1 ) { + *o++ = c; + s += 2; + } else + *o++ = '\\'; + } else + *o++ = *s++; + } + *o = '\0'; +} + +/* --- routine to convert 8bits characters to the "escaped" (\hh) form --- */ + +char *convert_8bit_to_escaped( s ) +Byte *s; +{ + Byte *o, *oo; + Byte n; + + if ( (o = oo = (Byte *)NSLDAPI_MALLOC( 2 * strlen( s ) + 64 )) == NULL ) { + return( NULL ); + } + + while ( *s ) { + if ( *s < 0x80 ) + *o++ = *s++; + else { + *o++ = '\\'; + n = *s >> 4; + *o++ = ((n < 0xA) ? '0' : 'A' - 0xA) + n; + n = *s++ & 0x0F; + *o++ = ((n < 0xA) ? '0' : 'A' - 0xA) + n; + } + } + *o = '\0'; + + o = oo; + + if ( (oo = (Byte *)NSLDAPI_REALLOC( o, strlen( o ) + 1 )) == NULL ) { + NSLDAPI_FREE( o ); + return( NULL ); + } + + return( (char *)oo ); +} + +/* --- routine to convert from T.61 to printable characters --- */ + +/* + printable characters [RFC 1488]: 'A'..'Z', 'a'..'z', '0'..'9', + '\'', '(', ')', '+', ',', '-', '.', '/', ':', '?, ' '. + + that conversion is language dependent. +*/ + +static Couple last_t61_printabled[32] = { + {0,0}, {'A','E'}, {'D',0}, {0,0}, + {'H',0}, {0,0}, {'I','J'}, {'L',0}, + {'L',0}, {'O',0}, {'O','E'}, {0,0}, + {'T','H'}, {'T',0}, {'N','G'}, {'n',0}, + {'k',0}, {'a','e'}, {'d',0}, {'d',0}, + {'h',0}, {'i',0}, {'i','j'}, {'l',0}, + {'l',0}, {'o',0}, {'o','e'}, {'s','s'}, + {'t','h'}, {'t',0}, {'n','g'}, {0,0} +}; + +char *t61_printable( s ) +Byte *s; +{ + Byte *o, *oo; + Byte n; + Couple *cc; + + if ( (o = oo = (Byte *)NSLDAPI_MALLOC( 2 * strlen( s ) + 64 )) == NULL ) { + return( NULL ); + } + + while ( *s ) { + if ( ( (*s >= 'A') && (*s <= 'Z') ) || + ( (*s >= 'a') && (*s <= 'z') ) || + ( (*s >= '0') && (*s <= '9') ) || + ( (*s >= '\'') && (*s <= ')') ) || + ( (*s >= '+') && (*s <= '/') ) || + ( *s == '?' ) || ( *s == ' ' ) ) + *o++ = *s++; + else { + if ( *s >= 0xE0 ) { + if ( (*(cc = &last_t61_printabled[ *s - 0xE0 ])).a ) { + *o++ = (*cc).a; + if ( (*cc).b ) *o++ = (*cc).b; + } + } + else if ( (*s >> 4) == 0xC ) { + switch ( *s ) { + case 0xCA: /* ring */ + switch ( *(s + 1) ) { + case 'A': *o++ = 'A'; *o++ = 'A'; s++; break; /* Swedish */ + case 'a': *o++ = 'a'; *o++ = 'a'; s++; break; /* Swedish */ + } + break; + + case 0xC8: /* diaeresis */ + switch ( *(s + 1) ) { + case 'Y': *o++ = 'I'; *o++ = 'J'; s++; break; /* Dutch */ + case 'y': *o++ = 'i'; *o++ = 'j'; s++; break; /* Dutch */ + } + break; + } + } + s++; + } + } + *o = '\0'; + + o = oo; + + if ( (oo = (Byte *)NSLDAPI_REALLOC( o, strlen( o ) + 1 )) == NULL ) { + NSLDAPI_FREE( o ); + return( NULL ); + } + + return( (char *)oo ); +} +#endif /* NOT_NEEDED_IN_LIBLDAP */ /* mcs@umich.edu 12 Oct 1995 */ + +#endif /* LDAP_CHARSET_8859 */ +#endif /* STR_TRANSLATION */ diff --git a/ldap/c-sdk/libraries/libldap/cldap.c b/ldap/c-sdk/libraries/libldap/cldap.c new file mode 100644 index 0000000000..829d34c287 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/cldap.c @@ -0,0 +1,585 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990, 1994 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * cldap.c - synchronous, retrying interface to the cldap protocol + */ + + +#ifdef CLDAP + +XXX not MT-safe XXX + +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990, 1994 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif + +#include +#include +#include +#ifdef macintosh +#include +#include "macos.h" +#else /* macintosh */ +#ifdef DOS +#include "msdos.h" +#else /* DOS */ +#ifdef _WINDOWS +#include +#else /* _WINDOWS */ +#include +#include +#include +#include +#include +#endif /* _WINDOWS */ +#endif /* DOS */ +#endif /* macintosh */ + +#include "ldap-int.h" + +#define DEF_CLDAP_TIMEOUT 3 +#define DEF_CLDAP_TRIES 4 + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK ((unsigned long) 0x7f000001) +#endif + + +struct cldap_retinfo { + int cri_maxtries; + int cri_try; + int cri_useaddr; + long cri_timeout; +}; + +#ifdef NEEDPROTOS +static int add_addr( LDAP *ld, struct sockaddr *sap ); +static int cldap_result( LDAP *ld, int msgid, LDAPMessage **res, + struct cldap_retinfo *crip, char *base ); +static int cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber, + LDAPMessage **res, char *base ); +#else /* NEEDPROTOS */ +static int add_addr(); +static int cldap_result(); +static int cldap_parsemsg(); +#endif /* NEEDPROTOS */ + +/* + * cldap_open - initialize and connect to an ldap server. A magic cookie to + * be used for future communication is returned on success, NULL on failure. + * + * Example: + * LDAP *ld; + * ld = cldap_open( hostname, port ); + */ + +LDAP * +cldap_open( char *host, int port ) +{ + int s; + ldap_x_in_addr_t address; + struct sockaddr_in sock; + struct hostent *hp; + LDAP *ld; + char *p; + int i; + + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_open\n", 0, 0, 0 ); + + if ( port == 0 ) { + port = LDAP_PORT; + } + + if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) { + return( NULL ); + } + + sock.sin_addr.s_addr = 0; + sock.sin_family = AF_INET; + sock.sin_port = 0; + if ( bind(s, (struct sockaddr *) &sock, sizeof(sock)) < 0) { + close( s ); + return( NULL ); + } + + if (( ld = ldap_init( host, port )) == NULL ) { + close( s ); + return( NULL ); + } + if ( (ld->ld_sbp->sb_fromaddr = (void *)NSLDAPI_CALLOC( 1, + sizeof( struct sockaddr ))) == NULL ) { + NSLDAPI_FREE( ld ); + close( s ); + return( NULL ); + } + ld->ld_sbp->sb_sd = s; + ld->ld_sbp->sb_naddr = 0; + ld->ld_version = LDAP_VERSION2; + + sock.sin_family = AF_INET; + sock.sin_port = htons( port ); + + /* + * 'host' may be a space-separated list. + */ + if ( host != NULL ) { + for ( ; host != NULL; host = p ) { + if (( p = strchr( host, ' ' )) != NULL ) { + for (*p++ = '\0'; *p == ' '; p++) { + ; + } + } + + if ( (address = inet_addr( host )) == -1 ) { +/* XXXmcs: need to use DNS callbacks here XXX */ +XXX + if ( (hp = gethostbyname( host )) == NULL ) { + LDAP_SET_ERRNO( ld, EHOSTUNREACH ); + continue; + } + + for ( i = 0; hp->h_addr_list[ i ] != 0; ++i ) { + SAFEMEMCPY( (char *)&sock.sin_addr.s_addr, + (char *)hp->h_addr_list[ i ], + sizeof(sock.sin_addr.s_addr)); + if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) { + close( s ); + NSLDAPI_FREE( ld ); + return( NULL ); + } + } + + } else { + sock.sin_addr.s_addr = address; + if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) { + close( s ); + NSLDAPI_FREE( ld ); + return( NULL ); + } + } + + if ( ld->ld_host == NULL ) { + ld->ld_host = nsldapi_strdup( host ); + } + } + + } else { + address = INADDR_LOOPBACK; + sock.sin_addr.s_addr = htonl( address ); + if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) { + close( s ); + NSLDAPI_FREE( ld ); + return( NULL ); + } + } + + if ( ld->ld_sbp->sb_addrs == NULL + || ( ld->ld_defconn = nsldapi_new_connection( ld, NULL, 1,0,0 )) == NULL ) { + NSLDAPI_FREE( ld ); + return( NULL ); + } + + ld->ld_sbp->sb_useaddr = ld->ld_sbp->sb_addrs[ 0 ]; + cldap_setretryinfo( ld, 0, 0 ); + +#ifdef LDAP_DEBUG + putchar( '\n' ); + for ( i = 0; i < ld->ld_sbp->sb_naddr; ++i ) { + LDAPDebug( LDAP_DEBUG_TRACE, "end of cldap_open address %d is %s\n", + i, inet_ntoa( ((struct sockaddr_in *) + ld->ld_sbp->sb_addrs[ i ])->sin_addr ), 0 ); + } +#endif + + return( ld ); +} + + + +void +cldap_close( LDAP *ld ) +{ + ldap_ld_free( ld, NULL, NULL, 0 ); +} + + +void +cldap_setretryinfo( LDAP *ld, int tries, int timeout ) +{ + ld->ld_cldaptries = ( tries <= 0 ) ? DEF_CLDAP_TRIES : tries; + ld->ld_cldaptimeout = ( timeout <= 0 ) ? DEF_CLDAP_TIMEOUT : timeout; +} + + +int +cldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs, + int attrsonly, LDAPMessage **res, char *logdn ) +{ + int ret, msgid; + struct cldap_retinfo cri; + + *res = NULLMSG; + + (void) memset( &cri, 0, sizeof( cri )); + + if ( logdn != NULL ) { + ld->ld_cldapdn = logdn; + } else if ( ld->ld_cldapdn == NULL ) { + ld->ld_cldapdn = ""; + } + + do { + if ( cri.cri_try != 0 ) { + --ld->ld_msgid; /* use same id as before */ + } + ld->ld_sbp->sb_useaddr = ld->ld_sbp->sb_addrs[ cri.cri_useaddr ]; + + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_search_s try %d (to %s)\n", + cri.cri_try, inet_ntoa( ((struct sockaddr_in *) + ld->ld_sbp->sb_useaddr)->sin_addr ), 0 ); + + if ( (msgid = ldap_search( ld, base, scope, filter, attrs, + attrsonly )) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } +#ifndef NO_CACHE + if ( ld->ld_cache != NULL && ld->ld_responses != NULL ) { + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_search_s res from cache\n", + 0, 0, 0 ); + *res = ld->ld_responses; + ld->ld_responses = ld->ld_responses->lm_next; + return( ldap_result2error( ld, *res, 0 )); + } +#endif /* NO_CACHE */ + ret = cldap_result( ld, msgid, res, &cri, base ); + } while (ret == -1); + + return( ret ); +} + + +static int +add_addr( LDAP *ld, struct sockaddr *sap ) +{ + struct sockaddr *newsap, **addrs; + + if (( newsap = (struct sockaddr *)NSLDAPI_MALLOC( + sizeof( struct sockaddr ))) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + + if ( ld->ld_sbp->sb_naddr == 0 ) { + addrs = (struct sockaddr **)NSLDAPI_MALLOC( sizeof(struct sockaddr *)); + } else { + addrs = (struct sockaddr **)NSLDAPI_REALLOC( ld->ld_sbp->sb_addrs, + ( ld->ld_sbp->sb_naddr + 1 ) * sizeof(struct sockaddr *)); + } + + if ( addrs == NULL ) { + NSLDAPI_FREE( newsap ); + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + + SAFEMEMCPY( (char *)newsap, (char *)sap, sizeof( struct sockaddr )); + addrs[ ld->ld_sbp->sb_naddr++ ] = newsap; + ld->ld_sbp->sb_addrs = (void **)addrs; + return( 0 ); +} + + +static int +cldap_result( LDAP *ld, int msgid, LDAPMessage **res, + struct cldap_retinfo *crip, char *base ) +{ + Sockbuf *sb = ld->ld_sbp; + BerElement ber; + char *logdn; + int ret, fromaddr, i; + ber_int_t id; + struct timeval tv; + + fromaddr = -1; + + if ( crip->cri_try == 0 ) { + crip->cri_maxtries = ld->ld_cldaptries * sb->sb_naddr; + crip->cri_timeout = ld->ld_cldaptimeout; + crip->cri_useaddr = 0; + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_result tries %d timeout %d\n", + ld->ld_cldaptries, ld->ld_cldaptimeout, 0 ); + } + + if ((tv.tv_sec = crip->cri_timeout / sb->sb_naddr) < 1 ) { + tv.tv_sec = 1; + } + tv.tv_usec = 0; + + LDAPDebug( LDAP_DEBUG_TRACE, + "cldap_result waiting up to %d seconds for a response\n", + tv.tv_sec, 0, 0 ); + ber_init_w_nullchar( &ber, 0 ); + nsldapi_set_ber_options( ld, &ber ); + + if ( cldap_getmsg( ld, &tv, &ber ) == -1 ) { + ret = LDAP_GET_LDERRNO( ld, NULL, NULL ); + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_getmsg returned -1 (%d)\n", + ret, 0, 0 ); + } else if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "cldap_result timed out\n", 0, 0, 0 ); + /* + * It timed out; is it time to give up? + */ + if ( ++crip->cri_try >= crip->cri_maxtries ) { + ret = LDAP_TIMEOUT; + --crip->cri_try; + } else { + if ( ++crip->cri_useaddr >= sb->sb_naddr ) { + /* + * new round: reset address to first one and + * double the timeout + */ + crip->cri_useaddr = 0; + crip->cri_timeout <<= 1; + } + ret = -1; + } + + } else { + /* + * Got a response. It should look like: + * { msgid, logdn, { searchresponse...}} + */ + logdn = NULL; + + if ( ber_scanf( &ber, "ia", &id, &logdn ) == LBER_ERROR ) { + NSLDAPI_FREE( ber.ber_buf ); /* gack! */ + ret = LDAP_DECODING_ERROR; + LDAPDebug( LDAP_DEBUG_TRACE, + "cldap_result: ber_scanf returned LBER_ERROR (%d)\n", + ret, 0, 0 ); + } else if ( id != msgid ) { + NSLDAPI_FREE( ber.ber_buf ); /* gack! */ + LDAPDebug( LDAP_DEBUG_TRACE, + "cldap_result: looking for msgid %d; got %ld\n", + msgid, id, 0 ); + ret = -1; /* ignore and keep looking */ + } else { + /* + * got a result: determine which server it came from + * decode into ldap message chain + */ + for ( fromaddr = 0; fromaddr < sb->sb_naddr; ++fromaddr ) { + if ( memcmp( &((struct sockaddr_in *) + sb->sb_addrs[ fromaddr ])->sin_addr, + &((struct sockaddr_in *)sb->sb_fromaddr)->sin_addr, + sizeof( struct in_addr )) == 0 ) { + break; + } + } + ret = cldap_parsemsg( ld, msgid, &ber, res, base ); + NSLDAPI_FREE( ber.ber_buf ); /* gack! */ + LDAPDebug( LDAP_DEBUG_TRACE, + "cldap_result got result (%d)\n", ret, 0, 0 ); + } + + if ( logdn != NULL ) { + NSLDAPI_FREE( logdn ); + } + } + + + /* + * If we are giving up (successfully or otherwise) then + * abandon any outstanding requests. + */ + if ( ret != -1 ) { + i = crip->cri_try; + if ( i >= sb->sb_naddr ) { + i = sb->sb_naddr - 1; + } + + for ( ; i >= 0; --i ) { + if ( i == fromaddr ) { + continue; + } + sb->sb_useaddr = sb->sb_addrs[ i ]; + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_result abandoning id %d (to %s)\n", + msgid, inet_ntoa( ((struct sockaddr_in *) + sb->sb_useaddr)->sin_addr ), 0 ); + (void) ldap_abandon( ld, msgid ); + } + } + + LDAP_SET_LDERRNO( ld, ret, NULL, NULL ); + return( ret ); +} + + +static int +cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber, + LDAPMessage **res, char *base ) +{ + ber_tag_t tag; + ber_len_t len; + int baselen, slen, rc; + char *dn, *p, *cookie; + LDAPMessage *chain, *prev, *ldm; + struct berval *bv; + + rc = LDAP_DECODING_ERROR; /* pessimistic */ + ldm = chain = prev = NULLMSG; + baselen = ( base == NULL ) ? 0 : strlen( base ); + bv = NULL; + + for ( tag = ber_first_element( ber, &len, &cookie ); + tag != LBER_ERROR && tag != LBER_END_OF_SEQOFSET + && rc != LDAP_SUCCESS; + tag = ber_next_element( ber, &len, cookie )) { + if (( ldm = (LDAPMessage *)NSLDAPI_CALLOC( 1, sizeof(LDAPMessage))) + == NULL ) { + rc = LDAP_NO_MEMORY; + break; /* return with error */ + } else if (( rc = nsldapi_alloc_ber_with_options( ld, &ldm->lm_ber )) + != LDAP_SUCCESS ) { + break; /* return with error*/ + } + ldm->lm_msgid = msgid; + ldm->lm_msgtype = tag; + + if ( tag == LDAP_RES_SEARCH_RESULT ) { + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg got search result\n", + 0, 0, 0 ); + + if ( ber_get_stringal( ber, &bv ) == LBER_DEFAULT ) { + break; /* return w/error */ + } + + if ( ber_printf( ldm->lm_ber, "to", tag, bv->bv_val, + bv->bv_len ) == -1 ) { + break; /* return w/error */ + } + ber_bvfree( bv ); + bv = NULL; + rc = LDAP_SUCCESS; + + } else if ( tag == LDAP_RES_SEARCH_ENTRY ) { + if ( ber_scanf( ber, "{aO", &dn, &bv ) == LBER_ERROR ) { + break; /* return w/error */ + } + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg entry %s\n", dn, 0, 0 ); + if ( dn != NULL && *(dn + ( slen = strlen(dn)) - 1) == '*' && + baselen > 0 ) { + /* + * substitute original searchbase for trailing '*' + */ + if (( p = (char *)NSLDAPI_MALLOC( slen + baselen )) == NULL ) { + rc = LDAP_NO_MEMORY; + NSLDAPI_FREE( dn ); + break; /* return w/error */ + } + strcpy( p, dn ); + strcpy( p + slen - 1, base ); + NSLDAPI_FREE( dn ); + dn = p; + } + + if ( ber_printf( ldm->lm_ber, "t{so}", tag, dn, bv->bv_val, + bv->bv_len ) == -1 ) { + break; /* return w/error */ + } + NSLDAPI_FREE( dn ); + ber_bvfree( bv ); + bv = NULL; + + } else { + LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg got unknown tag %d\n", + tag, 0, 0 ); + rc = LDAP_PROTOCOL_ERROR; + break; /* return w/error */ + } + + /* Reset message ber so we can read from it later. Gack! */ + ldm->lm_ber->ber_end = ldm->lm_ber->ber_ptr; + ldm->lm_ber->ber_ptr = ldm->lm_ber->ber_buf; + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + char msg[80]; + sprintf( msg, "cldap_parsemsg add message id %d type %d:\n", + ldm->lm_msgid, ldm->lm_msgtype ); + ber_err_print( msg ); + ber_dump( ldm->lm_ber, 1 ); + } +#endif /* LDAP_DEBUG */ + +#ifndef NO_CACHE + if ( ld->ld_cache != NULL ) { + nsldapi_add_result_to_cache( ld, ldm ); + } +#endif /* NO_CACHE */ + + if ( chain == NULL ) { + chain = ldm; + } else { + prev->lm_chain = ldm; + } + prev = ldm; + ldm = NULL; + } + + /* dispose of any leftovers */ + if ( ldm != NULL ) { + if ( ldm->lm_ber != NULLBER ) { + ber_free( ldm->lm_ber, 1 ); + } + NSLDAPI_FREE( ldm ); + } + if ( bv != NULL ) { + ber_bvfree( bv ); + } + + /* return chain, calling result2error if we got anything at all */ + *res = chain; + return(( *res == NULLMSG ) ? rc : ldap_result2error( ld, *res, 0 )); +} +#endif /* CLDAP */ diff --git a/ldap/c-sdk/libraries/libldap/compare.c b/ldap/c-sdk/libraries/libldap/compare.c new file mode 100644 index 0000000000..6ccd0e8aa5 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/compare.c @@ -0,0 +1,194 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * compare.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_compare - perform an ldap compare operation. The dn + * of the entry to compare to and the attribute and value to compare (in + * attr and value) are supplied. The msgid of the response is returned. + * + * Example: + * ldap_compare( ld, "c=us@cn=bob", "userPassword", "secret" ) + */ +int +LDAP_CALL +ldap_compare( LDAP *ld, const char *dn, const char *attr, const char *value ) +{ + int msgid; + struct berval bv; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_compare\n", 0, 0, 0 ); + + bv.bv_val = (char *)value; + bv.bv_len = ( value == NULL ) ? 0 : strlen( value ); + + if ( ldap_compare_ext( ld, dn, attr, &bv, NULL, NULL, &msgid ) + == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + +int +LDAP_CALL +ldap_compare_ext( LDAP *ld, const char *dn, const char *attr, + const struct berval *bvalue, LDAPControl **serverctrls, + LDAPControl **clientctrls, int *msgidp ) +{ + BerElement *ber; + int rc, lderr; + + /* The compare request looks like this: + * CompareRequest ::= SEQUENCE { + * entry DistinguishedName, + * ava SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * } + * and must be wrapped in an LDAPMessage. + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_compare_ext\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + if ( attr == NULL || bvalue == NULL || bvalue->bv_len == 0 + || msgidp == NULL ) { + lderr = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + return( lderr ); + } + + if ( dn == NULL ) { + dn = ""; + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* check the cache */ + if ( ld->ld_cache_on && ld->ld_cache_compare != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_compare)( ld, *msgidp, + LDAP_REQ_COMPARE, dn, attr, bvalue )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* create a message to send */ + if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( lderr ); + } + + if ( ber_printf( ber, "{it{s{so}}", *msgidp, LDAP_REQ_COMPARE, dn, + attr, bvalue->bv_val, bvalue->bv_len ) + == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + if (( lderr = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( lderr ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_COMPARE, + (char *)dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +int +LDAP_CALL +ldap_compare_s( LDAP *ld, const char *dn, const char *attr, + const char *value ) +{ + struct berval bv; + + bv.bv_val = (char *)value; + bv.bv_len = ( value == NULL ) ? 0 : strlen( value ); + + return( ldap_compare_ext_s( ld, dn, attr, &bv, NULL, NULL )); +} + +int +LDAP_CALL +ldap_compare_ext_s( LDAP *ld, const char *dn, const char *attr, + const struct berval *bvalue, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + int err, msgid; + LDAPMessage *res; + + if (( err = ldap_compare_ext( ld, dn, attr, bvalue, serverctrls, + clientctrls, &msgid )) != LDAP_SUCCESS ) { + return( err ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, &res ) + == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/compat.c b/ldap/c-sdk/libraries/libldap/compat.c new file mode 100644 index 0000000000..94e58ca7a3 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/compat.c @@ -0,0 +1,108 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1994 The Regents of the University of Michigan. + * All rights reserved. + */ +/* + * compat.c - compatibility routines. + * + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1994 The Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +#if defined( HPUX10 ) && defined( _REENTRANT ) && !defined(HPUX11) +extern int h_errno; + +struct hostent * +nsldapi_compat_gethostbyname_r( const char *name, struct hostent *result, + char *buffer, int buflen, int *h_errnop ) +{ + struct hostent_data *hep; + + if ( buflen < sizeof(struct hostent_data)) { /* sanity check */ + *h_errnop = NO_RECOVERY; /* XXX best error code to use? */ + return( NULL ); + } + + hep = (struct hostent_data *)buffer; + hep->current = NULL; + + if ( gethostbyname_r( name, result, hep ) == -1) { + *h_errnop = h_errno; /* XXX don't see anywhere else to get this */ + return NULL; + } + return result; +} + +char * +nsldapi_compat_ctime_r( const time_t *clock, char *buf, int buflen ) +{ + NSLDAPI_CTIME1( clock, buf, buflen ); + return buf; +} +#endif /* HPUX10 && _REENTRANT && !HPUX11 */ + +#if defined(LINUX) || defined(AIX) || defined(HPUX) || defined(_WINDOWS) +/* + * Copies src to the dstsize buffer at dst. The copy will never + * overflow the destination buffer and the buffer will always be null + * terminated. + */ +size_t nsldapi_compat_strlcpy(char *dst, const char *src, size_t len) +{ + size_t slen = strlen(src); + size_t copied; + + if (len == 0) + return (slen); + + if (slen >= len) + copied = len - 1; + else + copied = slen; + SAFEMEMCPY(dst, src, copied); + dst[copied] = '\0'; + return (slen); +} +#endif diff --git a/ldap/c-sdk/libraries/libldap/control.c b/ldap/c-sdk/libraries/libldap/control.c new file mode 100644 index 0000000000..5f2eb73758 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/control.c @@ -0,0 +1,559 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* control.c - routines to handle ldapv3 controls */ + +#include "ldap-int.h" + +static LDAPControl *ldap_control_dup( LDAPControl *ctrl ); +static int ldap_control_copy_contents( LDAPControl *ctrl_dst, + LDAPControl *ctrl_src ); + +/* + * Append a list of LDAPv3 controls to ber. If ctrls is NULL, use default + * set of controls from ld. + * Return an LDAP error code (LDAP_SUCCESS if all goes well). + * If closeseq is non-zero, we do an extra ber_put_seq() as well. + */ +int +nsldapi_put_controls( LDAP *ld, LDAPControl **ctrls, int closeseq, + BerElement *ber ) +{ + LDAPControl *c; + int rc, i; + + rc = LDAP_ENCODING_ERROR; /* the most popular error */ + + /* if no controls were passed in, use global list from LDAP * */ + LDAP_MUTEX_LOCK( ld, LDAP_CTRL_LOCK ); + if ( ctrls == NULL ) { + ctrls = ld->ld_servercontrols; + } + + /* if there are no controls then we are done */ + if ( ctrls == NULL || ctrls[ 0 ] == NULL ) { + goto clean_exit; + } + + /* + * If we're using LDAPv2 or earlier we can't send any controls, so + * we just ignore them unless one is marked critical, in which case + * we return an error. + */ + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + for ( i = 0; ctrls != NULL && ctrls[i] != NULL; i++ ) { + if ( ctrls[i]->ldctl_iscritical ) { + rc = LDAP_NOT_SUPPORTED; + goto error_exit; + } + } + goto clean_exit; + } + + /* + * encode the controls as a Sequence of Sequence + */ + if ( ber_printf( ber, "t{", LDAP_TAG_CONTROLS ) == -1 ) { + goto error_exit; + } + + for ( i = 0; ctrls[i] != NULL; i++ ) { + c = ctrls[i]; + + if ( ber_printf( ber, "{s", c->ldctl_oid ) == -1 ) { + goto error_exit; + } + + /* criticality is "BOOLEAN DEFAULT FALSE" */ + /* therefore, it should only be encoded if it exists AND is TRUE */ + if ( c->ldctl_iscritical ) { + if ( ber_printf( ber, "b", (int)c->ldctl_iscritical ) + == -1 ) { + goto error_exit; + } + } + + if ( c->ldctl_value.bv_val != NULL ) { + if ( ber_printf( ber, "o", c->ldctl_value.bv_val, + c->ldctl_value.bv_len ) + == -1 ) { + goto error_exit; + } + } + + if ( ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + } + + if ( ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + +clean_exit: + LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK ); + if ( closeseq && ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + return( LDAP_SUCCESS ); + +error_exit: + LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK ); + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + + +/* + * Pull controls out of "ber" (if any present) and return them in "controlsp." + * Returns an LDAP error code. + */ +int +nsldapi_get_controls( BerElement *ber, LDAPControl ***controlsp ) +{ + LDAPControl *newctrl; + ber_tag_t tag; + ber_len_t len; + int rc, maxcontrols, curcontrols; + char *last; + + /* + * Each LDAPMessage can have a set of controls appended + * to it. Controls are used to extend the functionality + * of an LDAP operation (e.g., add an attribute size limit + * to the search operation). These controls look like this: + * + * Controls ::= SEQUENCE OF Control + * + * Control ::= SEQUENCE { + * controlType LDAPOID, + * criticality BOOLEAN DEFAULT FALSE, + * controlValue OCTET STRING + * } + */ + LDAPDebug( LDAP_DEBUG_TRACE, "=> nsldapi_get_controls\n", 0, 0, 0 ); + + *controlsp = NULL; + + /* + * check to see if controls were included + */ + if ( ber_get_option( ber, LBER_OPT_REMAINING_BYTES, &len ) != 0 ) { + return( LDAP_DECODING_ERROR ); /* unexpected error */ + } + if ( len == 0 ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls no controls\n", 0, 0, 0 ); + return( LDAP_SUCCESS ); /* no controls */ + } + if (( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) { + if ( tag == LBER_ERROR ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls LDAP_PROTOCOL_ERROR\n", + 0, 0, 0 ); + return( LDAP_DECODING_ERROR ); /* decoding error */ + } + /* + * We found something other than controls. This should never + * happen in LDAPv3, but we don't treat this is a hard error -- + * we just ignore the extra stuff. + */ + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls ignoring unrecognized data in message (tag 0x%x)\n", + tag, 0, 0 ); + return( LDAP_SUCCESS ); + } + + maxcontrols = curcontrols = 0; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET; + tag = ber_next_element( ber, &len, last ) ) { + if ( curcontrols >= maxcontrols - 1 ) { +#define CONTROL_GRABSIZE 5 + maxcontrols += CONTROL_GRABSIZE; + *controlsp = (struct ldapcontrol **)NSLDAPI_REALLOC( + (char *)*controlsp, maxcontrols * + sizeof(struct ldapcontrol *) ); + if ( *controlsp == NULL ) { + rc = LDAP_NO_MEMORY; + goto free_and_return; + } + } + if (( newctrl = (struct ldapcontrol *)NSLDAPI_CALLOC( 1, + sizeof(LDAPControl))) == NULL ) { + rc = LDAP_NO_MEMORY; + goto free_and_return; + } + + (*controlsp)[curcontrols++] = newctrl; + (*controlsp)[curcontrols] = NULL; + + if ( ber_scanf( ber, "{a", &newctrl->ldctl_oid ) + == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + /* the criticality is optional */ + if ( ber_peek_tag( ber, &len ) == LBER_BOOLEAN ) { + int aint; + + if ( ber_scanf( ber, "b", &aint ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + newctrl->ldctl_iscritical = (char)aint; /* XXX lossy cast */ + } else { + /* absent is synonomous with FALSE */ + newctrl->ldctl_iscritical = 0; + } + + /* the control value is optional */ + if ( ber_peek_tag( ber, &len ) == LBER_OCTETSTRING ) { + if ( ber_scanf( ber, "o", &newctrl->ldctl_value ) + == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + } else { + (newctrl->ldctl_value).bv_val = NULL; + (newctrl->ldctl_value).bv_len = 0; + } + + } + + if ( tag == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls found %d controls\n", curcontrols, 0, 0 ); + return( LDAP_SUCCESS ); + +free_and_return:; + ldap_controls_free( *controlsp ); + *controlsp = NULL; + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls error 0x%x\n", rc, 0, 0 ); + return( rc ); +} + +/* + * Skips forward in a ber to find a control tag, then calls on + * nsldapi_get_controls() to parse them into an LDAPControl list. + * Returns an LDAP error code. + */ +int +nsldapi_find_controls( BerElement *ber, LDAPControl ***controlsp ) +{ + ber_tag_t tag; + ber_len_t len; + + if ( ber == NULLBER ) { + return( LDAP_DECODING_ERROR ); + } + + tag = ber_peek_tag( ber, &len ); + + while( tag != LDAP_TAG_CONTROLS && tag != LBER_DEFAULT ) { + tag = ber_skip_tag( ber, &len ); + /* Skip ahead to the next sequence */ + ber->ber_ptr += len; + tag = ber_peek_tag( ber, &len ); + } + + return( nsldapi_get_controls( ber, controlsp ) ); +} + + +void +LDAP_CALL +ldap_control_free( LDAPControl *ctrl ) +{ + if ( ctrl != NULL ) { + if ( ctrl->ldctl_oid != NULL ) { + NSLDAPI_FREE( ctrl->ldctl_oid ); + } + if ( ctrl->ldctl_value.bv_val != NULL ) { + NSLDAPI_FREE( ctrl->ldctl_value.bv_val ); + } + NSLDAPI_FREE( (char *)ctrl ); + } +} + + +void +LDAP_CALL +ldap_controls_free( LDAPControl **ctrls ) +{ + int i; + + if ( ctrls != NULL ) { + for ( i = 0; ctrls[i] != NULL; i++ ) { + ldap_control_free( ctrls[i] ); + } + NSLDAPI_FREE( (char *)ctrls ); + } +} + +LDAPControl * +LDAP_CALL +ldap_find_control( const char *oid, LDAPControl **ctrls ) +{ + int i, foundControl; + LDAPControl *Ctrlp = NULL; + + /* find the control in the list of controls if it exists */ + if ( ctrls == NULL ) { + return ( NULL ); + } + foundControl = 0; + for ( i = 0; (( ctrls[i] != NULL ) && ( !foundControl )); i++ ) { + foundControl = !strcmp( ctrls[i]->ldctl_oid, oid ); + } + if ( !foundControl ) { + return ( NULL ); + } else { + /* let local var point to the control */ + Ctrlp = ctrls[i-1]; + } + + return( Ctrlp ); +} + +#if 0 +LDAPControl ** +LDAP_CALL +ldap_control_append( LDAPControl **ctrl_src, LDAPControl *ctrl ) +{ + int nctrls = 0; + LDAPControl **ctrlp; + int i; + + if ( NULL == ctrl ) + return ( NULL ); + + /* Count the existing controls */ + if ( NULL != ctrl_src ) { + while( NULL != ctrl_src[nctrls] ) { + nctrls++; + } + } + + /* allocate the new control structure */ + if ( ( ctrlp = (LDAPControl **)NSLDAPI_MALLOC( sizeof(LDAPControl *) + * (nctrls + 2) ) ) == NULL ) { + return( NULL ); + } + memset( ctrlp, 0, sizeof(*ctrlp) * (nctrls + 2) ); + + for( i = 0; i < (nctrls + 1); i++ ) { + if ( i < nctrls ) { + ctrlp[i] = ldap_control_dup( ctrl_src[i] ); + } else { + ctrlp[i] = ldap_control_dup( ctrl ); + } + if ( NULL == ctrlp[i] ) { + ldap_controls_free( ctrlp ); + return( NULL ); + } + } + return ctrlp; +} +#endif /* 0 */ + + +/* + * Replace *ldctrls with a copy of newctrls. + * returns 0 if successful. + * return -1 if not and set error code inside LDAP *ld. + */ +int +nsldapi_dup_controls( LDAP *ld, LDAPControl ***ldctrls, LDAPControl **newctrls ) +{ + int count; + + if ( *ldctrls != NULL ) { + ldap_controls_free( *ldctrls ); + } + + if ( newctrls == NULL || newctrls[0] == NULL ) { + *ldctrls = NULL; + return( 0 ); + } + + for ( count = 0; newctrls[ count ] != NULL; ++count ) { + ; + } + + if (( *ldctrls = (LDAPControl **)NSLDAPI_MALLOC(( count + 1 ) * + sizeof( LDAPControl *))) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + (*ldctrls)[ count ] = NULL; + + for ( count = 0; newctrls[ count ] != NULL; ++count ) { + if (( (*ldctrls)[ count ] = + ldap_control_dup( newctrls[ count ] )) == NULL ) { + ldap_controls_free( *ldctrls ); + *ldctrls = NULL; + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + } + + return( 0 ); +} + + +/* + * return a malloc'd copy of "ctrl" (NULL if memory allocation fails) + */ +static LDAPControl * +/* LDAP_CALL */ /* keep this routine internal for now */ +ldap_control_dup( LDAPControl *ctrl ) +{ + LDAPControl *rctrl; + + if (( rctrl = (LDAPControl *)NSLDAPI_MALLOC( sizeof( LDAPControl ))) + == NULL ) { + return( NULL ); + } + + if ( ldap_control_copy_contents( rctrl, ctrl ) != LDAP_SUCCESS ) { + NSLDAPI_FREE( rctrl ); + return( NULL ); + } + + return( rctrl ); +} + + +/* + * duplicate the contents of "ctrl_src" and place in "ctrl_dst" + */ +static int +/* LDAP_CALL */ /* keep this routine internal for now */ +ldap_control_copy_contents( LDAPControl *ctrl_dst, LDAPControl *ctrl_src ) +{ + size_t len; + + if ( NULL == ctrl_dst || NULL == ctrl_src ) { + return( LDAP_PARAM_ERROR ); + } + + ctrl_dst->ldctl_iscritical = ctrl_src->ldctl_iscritical; + + /* fill in the fields of this new control */ + if (( ctrl_dst->ldctl_oid = nsldapi_strdup( ctrl_src->ldctl_oid )) + == NULL ) { + return( LDAP_NO_MEMORY ); + } + + len = (size_t)(ctrl_src->ldctl_value).bv_len; + if ( ctrl_src->ldctl_value.bv_val == NULL || len <= 0 ) { + ctrl_dst->ldctl_value.bv_len = 0; + ctrl_dst->ldctl_value.bv_val = NULL; + } else { + ctrl_dst->ldctl_value.bv_len = len; + if (( ctrl_dst->ldctl_value.bv_val = NSLDAPI_MALLOC( len )) + == NULL ) { + NSLDAPI_FREE( ctrl_dst->ldctl_oid ); + return( LDAP_NO_MEMORY ); + } + SAFEMEMCPY( ctrl_dst->ldctl_value.bv_val, + ctrl_src->ldctl_value.bv_val, len ); + } + + return ( LDAP_SUCCESS ); +} + + + +/* + * build an allocated LDAPv3 control. Returns an LDAP error code. + */ +int +nsldapi_build_control( char *oid, BerElement *ber, int freeber, char iscritical, + LDAPControl **ctrlp ) +{ + int rc; + struct berval *bvp; + + if ( ber == NULL ) { + bvp = NULL; + } else { + /* allocate struct berval with contents of the BER encoding */ + rc = ber_flatten( ber, &bvp ); + if ( freeber ) { + ber_free( ber, 1 ); + } + if ( rc == -1 ) { + return( LDAP_NO_MEMORY ); + } + } + + /* allocate the new control structure */ + if (( *ctrlp = (LDAPControl *)NSLDAPI_MALLOC( sizeof(LDAPControl))) + == NULL ) { + if ( bvp != NULL ) { + ber_bvfree( bvp ); + } + return( LDAP_NO_MEMORY ); + } + + /* fill in the fields of this new control */ + (*ctrlp)->ldctl_iscritical = iscritical; + if (( (*ctrlp)->ldctl_oid = nsldapi_strdup( oid )) == NULL ) { + NSLDAPI_FREE( *ctrlp ); + if ( bvp != NULL ) { + ber_bvfree( bvp ); + } + return( LDAP_NO_MEMORY ); + } + + if ( bvp == NULL ) { + (*ctrlp)->ldctl_value.bv_len = 0; + (*ctrlp)->ldctl_value.bv_val = NULL; + } else { + (*ctrlp)->ldctl_value = *bvp; /* struct copy */ + NSLDAPI_FREE( bvp ); /* free container, not contents! */ + } + + return( LDAP_SUCCESS ); +} diff --git a/ldap/c-sdk/libraries/libldap/countvalues.c b/ldap/c-sdk/libraries/libldap/countvalues.c new file mode 100644 index 0000000000..90e9e0adc6 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/countvalues.c @@ -0,0 +1,67 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * countvalues.c + */ + +#include "ldap-int.h" + +int +LDAP_CALL +ldap_count_values( char **vals ) +{ + int i; + + if ( vals == NULL ) + return( 0 ); + + for ( i = 0; vals[i] != NULL; i++ ) + ; /* NULL */ + + return( i ); +} + +int +LDAP_CALL +ldap_count_values_len( struct berval **vals ) +{ + return( ldap_count_values( (char **) vals ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/delete.c b/ldap/c-sdk/libraries/libldap/delete.c new file mode 100644 index 0000000000..531761371b --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/delete.c @@ -0,0 +1,169 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * delete.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_delete - initiate an ldap delete operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to delete + * + * Example: + * msgid = ldap_delete( ld, dn ); + */ +int +LDAP_CALL +ldap_delete( LDAP *ld, const char *dn ) +{ + int msgid; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_delete\n", 0, 0, 0 ); + + if ( ldap_delete_ext( ld, dn, NULL, NULL, &msgid ) == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + +int +LDAP_CALL +ldap_delete_ext( LDAP *ld, const char *dn, LDAPControl **serverctrls, + LDAPControl **clientctrls, int *msgidp ) +{ + BerElement *ber; + int rc, lderr; + + /* + * A delete request looks like this: + * DelRequet ::= DistinguishedName, + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_delete_ext\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( !NSLDAPI_VALID_LDAPMESSAGE_POINTER( msgidp )) + { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + if ( dn == NULL ) { + dn = ""; + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* see if we should add to the cache */ + if ( ld->ld_cache_on && ld->ld_cache_delete != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_delete)( ld, *msgidp, LDAP_REQ_DELETE, + dn )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* create a message to send */ + if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( lderr ); + } + + if ( ber_printf( ber, "{its", *msgidp, LDAP_REQ_DELETE, dn ) + == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + if (( lderr = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( lderr ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_DELETE, + (char *)dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +int +LDAP_CALL +ldap_delete_s( LDAP *ld, const char *dn ) +{ + return( ldap_delete_ext_s( ld, dn, NULL, NULL )); +} + +int +LDAP_CALL +ldap_delete_ext_s( LDAP *ld, const char *dn, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + int err, msgid; + LDAPMessage *res; + + if (( err = ldap_delete_ext( ld, dn, serverctrls, clientctrls, + &msgid )) != LDAP_SUCCESS ) { + return( err ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, &res ) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/disptmpl.c b/ldap/c-sdk/libraries/libldap/disptmpl.c new file mode 100644 index 0000000000..17fc71944b --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/disptmpl.c @@ -0,0 +1,770 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* + * disptmpl.c: display template library routines for LDAP clients + */ + +#include "ldap-int.h" +#include "disptmpl.h" + +static void free_disptmpl( struct ldap_disptmpl *tmpl ); +static int read_next_tmpl( char **bufp, long *blenp, + struct ldap_disptmpl **tmplp, int dtversion ); + +static char *tmploptions[] = { + "addable", "modrdn", + "altview", + NULL +}; + + +static unsigned long tmploptvals[] = { + LDAP_DTMPL_OPT_ADDABLE, LDAP_DTMPL_OPT_ALLOWMODRDN, + LDAP_DTMPL_OPT_ALTVIEW, +}; + + +static char *itemtypes[] = { + "cis", "mls", "dn", + "bool", "jpeg", "jpegbtn", + "fax", "faxbtn", "audiobtn", + "time", "date", "url", + "searchact", "linkact", "adddnact", + "addact", "verifyact", "mail", + NULL +}; + +static unsigned long itemsynids[] = { + LDAP_SYN_CASEIGNORESTR, LDAP_SYN_MULTILINESTR, LDAP_SYN_DN, + LDAP_SYN_BOOLEAN, LDAP_SYN_JPEGIMAGE, LDAP_SYN_JPEGBUTTON, + LDAP_SYN_FAXIMAGE, LDAP_SYN_FAXBUTTON, LDAP_SYN_AUDIOBUTTON, + LDAP_SYN_TIME, LDAP_SYN_DATE, LDAP_SYN_LABELEDURL, + LDAP_SYN_SEARCHACTION, LDAP_SYN_LINKACTION, LDAP_SYN_ADDDNACTION, + LDAP_SYN_ADDDNACTION, LDAP_SYN_VERIFYDNACTION,LDAP_SYN_RFC822ADDR, +}; + + +static char *itemoptions[] = { + "ro", "sort", + "1val", "hide", + "required", "hideiffalse", + NULL +}; + + +static unsigned long itemoptvals[] = { + LDAP_DITEM_OPT_READONLY, LDAP_DITEM_OPT_SORTVALUES, + LDAP_DITEM_OPT_SINGLEVALUED, LDAP_DITEM_OPT_HIDEIFEMPTY, + LDAP_DITEM_OPT_VALUEREQUIRED, LDAP_DITEM_OPT_HIDEIFFALSE, +}; + + +#define ADDEF_CONSTANT "constant" +#define ADDEF_ADDERSDN "addersdn" + + +int +LDAP_CALL +ldap_init_templates( char *file, struct ldap_disptmpl **tmpllistp ) +{ + FILE *fp; + char *buf; + long rlen, len; + int rc, eof; + + *tmpllistp = NULLDISPTMPL; + + if (( fp = NSLDAPI_FOPEN( file, "r" )) == NULL ) { + return( LDAP_TMPL_ERR_FILE ); + } + + if ( fseek( fp, 0L, SEEK_END ) != 0 ) { /* move to end to get len */ + fclose( fp ); + return( LDAP_TMPL_ERR_FILE ); + } + + len = ftell( fp ); + + if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { /* back to start of file */ + fclose( fp ); + return( LDAP_TMPL_ERR_FILE ); + } + + if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) { + fclose( fp ); + return( LDAP_TMPL_ERR_MEM ); + } + + rlen = fread( buf, 1, (size_t)len, fp ); + eof = feof( fp ); + fclose( fp ); + + if ( rlen != len && !eof ) { /* error: didn't get the whole file */ + NSLDAPI_FREE( buf ); + return( LDAP_TMPL_ERR_FILE ); + } + + rc = ldap_init_templates_buf( buf, rlen, tmpllistp ); + NSLDAPI_FREE( buf ); + + return( rc ); +} + + +int +LDAP_CALL +ldap_init_templates_buf( char *buf, long buflen, + struct ldap_disptmpl **tmpllistp ) +{ + int rc = 0, version; + char **toks; + struct ldap_disptmpl *prevtmpl, *tmpl; + + *tmpllistp = prevtmpl = NULLDISPTMPL; + + if ( nsldapi_next_line_tokens( &buf, &buflen, &toks ) != 2 || + strcasecmp( toks[ 0 ], "version" ) != 0 ) { + nsldapi_free_strarray( toks ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + version = atoi( toks[ 1 ] ); + nsldapi_free_strarray( toks ); + if ( version != LDAP_TEMPLATE_VERSION ) { + return( LDAP_TMPL_ERR_VERSION ); + } + + while ( buflen > 0 && ( rc = read_next_tmpl( &buf, &buflen, &tmpl, + version )) == 0 && tmpl != NULLDISPTMPL ) { + if ( prevtmpl == NULLDISPTMPL ) { + *tmpllistp = tmpl; + } else { + prevtmpl->dt_next = tmpl; + } + prevtmpl = tmpl; + } + + if ( rc != 0 ) { + ldap_free_templates( *tmpllistp ); + } + + return( rc ); +} + + + +void +LDAP_CALL +ldap_free_templates( struct ldap_disptmpl *tmpllist ) +{ + struct ldap_disptmpl *tp, *nexttp; + + if ( tmpllist != NULL ) { + for ( tp = tmpllist; tp != NULL; tp = nexttp ) { + nexttp = tp->dt_next; + free_disptmpl( tp ); + } + } +} + + +static void +free_disptmpl( struct ldap_disptmpl *tmpl ) +{ + if ( tmpl != NULL ) { + if ( tmpl->dt_name != NULL ) { + NSLDAPI_FREE( tmpl->dt_name ); + } + + if ( tmpl->dt_pluralname != NULL ) { + NSLDAPI_FREE( tmpl->dt_pluralname ); + } + + if ( tmpl->dt_iconname != NULL ) { + NSLDAPI_FREE( tmpl->dt_iconname ); + } + + if ( tmpl->dt_authattrname != NULL ) { + NSLDAPI_FREE( tmpl->dt_authattrname ); + } + + if ( tmpl->dt_defrdnattrname != NULL ) { + NSLDAPI_FREE( tmpl->dt_defrdnattrname ); + } + + if ( tmpl->dt_defaddlocation != NULL ) { + NSLDAPI_FREE( tmpl->dt_defaddlocation ); + } + + if ( tmpl->dt_oclist != NULL ) { + struct ldap_oclist *ocp, *nextocp; + + for ( ocp = tmpl->dt_oclist; ocp != NULL; ocp = nextocp ) { + nextocp = ocp->oc_next; + nsldapi_free_strarray( ocp->oc_objclasses ); + NSLDAPI_FREE( ocp ); + } + } + + if ( tmpl->dt_adddeflist != NULL ) { + struct ldap_adddeflist *adp, *nextadp; + + for ( adp = tmpl->dt_adddeflist; adp != NULL; adp = nextadp ) { + nextadp = adp->ad_next; + if( adp->ad_attrname != NULL ) { + NSLDAPI_FREE( adp->ad_attrname ); + } + if( adp->ad_value != NULL ) { + NSLDAPI_FREE( adp->ad_value ); + } + NSLDAPI_FREE( adp ); + } + } + + if ( tmpl->dt_items != NULL ) { + struct ldap_tmplitem *rowp, *nextrowp, *colp, *nextcolp; + + for ( rowp = tmpl->dt_items; rowp != NULL; rowp = nextrowp ) { + nextrowp = rowp->ti_next_in_col; + for ( colp = rowp; colp != NULL; colp = nextcolp ) { + nextcolp = colp->ti_next_in_row; + if ( colp->ti_attrname != NULL ) { + NSLDAPI_FREE( colp->ti_attrname ); + } + if ( colp->ti_label != NULL ) { + NSLDAPI_FREE( colp->ti_label ); + } + if ( colp->ti_args != NULL ) { + nsldapi_free_strarray( colp->ti_args ); + } + NSLDAPI_FREE( colp ); + } + } + } + + NSLDAPI_FREE( tmpl ); + } +} + + +struct ldap_disptmpl * +LDAP_CALL +ldap_first_disptmpl( struct ldap_disptmpl *tmpllist ) +{ + return( tmpllist ); +} + + +struct ldap_disptmpl * +LDAP_CALL +ldap_next_disptmpl( struct ldap_disptmpl *tmpllist, + struct ldap_disptmpl *tmpl ) +{ + return( tmpl == NULLDISPTMPL ? tmpl : tmpl->dt_next ); +} + + +struct ldap_disptmpl * +LDAP_CALL +ldap_name2template( char *name, struct ldap_disptmpl *tmpllist ) +{ + struct ldap_disptmpl *dtp; + + for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL; + dtp = ldap_next_disptmpl( tmpllist, dtp )) { + if ( strcasecmp( name, dtp->dt_name ) == 0 ) { + return( dtp ); + } + } + + return( NULLDISPTMPL ); +} + + +struct ldap_disptmpl * +LDAP_CALL +ldap_oc2template( char **oclist, struct ldap_disptmpl *tmpllist ) +{ + struct ldap_disptmpl *dtp; + struct ldap_oclist *oclp; + int i, j, needcnt, matchcnt; + + if ( tmpllist == NULL || oclist == NULL || oclist[ 0 ] == NULL ) { + return( NULLDISPTMPL ); + } + + for ( dtp = ldap_first_disptmpl( tmpllist ); dtp != NULLDISPTMPL; + dtp = ldap_next_disptmpl( tmpllist, dtp )) { + for ( oclp = dtp->dt_oclist; oclp != NULLOCLIST; + oclp = oclp->oc_next ) { + needcnt = matchcnt = 0; + for ( i = 0; oclp->oc_objclasses[ i ] != NULL; ++i ) { + for ( j = 0; oclist[ j ] != NULL; ++j ) { + if ( strcasecmp( oclist[ j ], oclp->oc_objclasses[ i ] ) + == 0 ) { + ++matchcnt; + } + } + ++needcnt; + } + + if ( matchcnt == needcnt ) { + return( dtp ); + } + } + } + + return( NULLDISPTMPL ); +} + + +struct ldap_tmplitem * +LDAP_CALL +ldap_first_tmplrow( struct ldap_disptmpl *tmpl ) +{ + return( tmpl->dt_items ); +} + + +struct ldap_tmplitem * +LDAP_CALL +ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row ) +{ + return( row == NULLTMPLITEM ? row : row->ti_next_in_col ); +} + + +struct ldap_tmplitem * +LDAP_CALL +ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row ) +{ + return( row ); +} + + +struct ldap_tmplitem * +LDAP_CALL +ldap_next_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row, + struct ldap_tmplitem *col ) +{ + return( col == NULLTMPLITEM ? col : col->ti_next_in_row ); +} + + +char ** +LDAP_CALL +ldap_tmplattrs( struct ldap_disptmpl *tmpl, char **includeattrs, + int exclude, unsigned long syntaxmask ) +{ +/* + * this routine should filter out duplicate attributes... + */ + struct ldap_tmplitem *tirowp, *ticolp; + int i, attrcnt, memerr; + char **attrs; + + attrcnt = 0; + memerr = 0; + + if (( attrs = (char **)NSLDAPI_MALLOC( sizeof( char * ))) == NULL ) { + return( NULL ); + } + + if ( includeattrs != NULL ) { + for ( i = 0; !memerr && includeattrs[ i ] != NULL; ++i ) { + if (( attrs = (char **)NSLDAPI_REALLOC( attrs, ( attrcnt + 2 ) * + sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] = + nsldapi_strdup( includeattrs[ i ] )) == NULL ) { + memerr = 1; + } else { + attrs[ attrcnt ] = NULL; + } + } + } + + for ( tirowp = ldap_first_tmplrow( tmpl ); + !memerr && tirowp != NULLTMPLITEM; + tirowp = ldap_next_tmplrow( tmpl, tirowp )) { + for ( ticolp = ldap_first_tmplcol( tmpl, tirowp ); + ticolp != NULLTMPLITEM; + ticolp = ldap_next_tmplcol( tmpl, tirowp, ticolp )) { + + if ( syntaxmask != 0 ) { + if (( exclude && + ( syntaxmask & ticolp->ti_syntaxid ) != 0 ) || + ( !exclude && + ( syntaxmask & ticolp->ti_syntaxid ) == 0 )) { + continue; + } + } + + if ( ticolp->ti_attrname != NULL ) { + if (( attrs = (char **)NSLDAPI_REALLOC( attrs, ( attrcnt + 2 ) + * sizeof( char * ))) == NULL || ( attrs[ attrcnt++ ] = + nsldapi_strdup( ticolp->ti_attrname )) == NULL ) { + memerr = 1; + } else { + attrs[ attrcnt ] = NULL; + } + } + } + } + + if ( memerr || attrcnt == 0 ) { + for ( i = 0; i < attrcnt; ++i ) { + if ( attrs[ i ] != NULL ) { + NSLDAPI_FREE( attrs[ i ] ); + } + } + + NSLDAPI_FREE( (char *)attrs ); + return( NULL ); + } + + return( attrs ); +} + + +static int +read_next_tmpl( char **bufp, long *blenp, struct ldap_disptmpl **tmplp, + int dtversion ) +{ + int i, j, tokcnt, samerow, adsource; + char **toks, *itemopts; + struct ldap_disptmpl *tmpl = NULL; + struct ldap_oclist *ocp = NULL, *prevocp = NULL; + struct ldap_adddeflist *adp = NULL, *prevadp = NULL; + struct ldap_tmplitem *rowp = NULL, *ip = NULL, *previp = NULL; + + /* + * template name comes first + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + return( tokcnt == 0 ? 0 : LDAP_TMPL_ERR_SYNTAX ); + } + + if (( tmpl = (struct ldap_disptmpl *)NSLDAPI_CALLOC( 1, + sizeof( struct ldap_disptmpl ))) == NULL ) { + nsldapi_free_strarray( toks ); + return( LDAP_TMPL_ERR_MEM ); + } + tmpl->dt_name = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * template plural name comes next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + tmpl->dt_pluralname = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * template icon name is next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + tmpl->dt_iconname = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * template options come next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) < 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + for ( i = 0; toks[ i ] != NULL; ++i ) { + for ( j = 0; tmploptions[ j ] != NULL; ++j ) { + if ( strcasecmp( toks[ i ], tmploptions[ j ] ) == 0 ) { + tmpl->dt_options |= tmploptvals[ j ]; + } + } + } + nsldapi_free_strarray( toks ); + + /* + * object class list is next + */ + while (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) > 0 ) { + if (( ocp = (struct ldap_oclist *)NSLDAPI_CALLOC( 1, + sizeof( struct ldap_oclist ))) == NULL ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_MEM ); + } + ocp->oc_objclasses = toks; + if ( tmpl->dt_oclist == NULL ) { + tmpl->dt_oclist = ocp; + } else { + prevocp->oc_next = ocp; + } + prevocp = ocp; + } + if ( tokcnt < 0 ) { + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + + /* + * read name of attribute to authenticate as + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + if ( toks[ 0 ][ 0 ] != '\0' ) { + tmpl->dt_authattrname = toks[ 0 ]; + } else { + NSLDAPI_FREE( toks[ 0 ] ); + } + NSLDAPI_FREE( (char *)toks ); + + /* + * read default attribute to use for RDN + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + tmpl->dt_defrdnattrname = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * read default location for new entries + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + if ( toks[ 0 ][ 0 ] != '\0' ) { + tmpl->dt_defaddlocation = toks[ 0 ]; + } else { + NSLDAPI_FREE( toks[ 0 ] ); + } + NSLDAPI_FREE( (char *)toks ); + + /* + * read list of rules used to define default values for new entries + */ + while (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) > 0 ) { + if ( strcasecmp( ADDEF_CONSTANT, toks[ 0 ] ) == 0 ) { + adsource = LDAP_ADSRC_CONSTANTVALUE; + } else if ( strcasecmp( ADDEF_ADDERSDN, toks[ 0 ] ) == 0 ) { + adsource = LDAP_ADSRC_ADDERSDN; + } else { + adsource = 0; + } + if ( adsource == 0 || tokcnt < 2 || + ( adsource == LDAP_ADSRC_CONSTANTVALUE && tokcnt != 3 ) || + ( adsource == LDAP_ADSRC_ADDERSDN && tokcnt != 2 )) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + + if (( adp = (struct ldap_adddeflist *)NSLDAPI_CALLOC( 1, + sizeof( struct ldap_adddeflist ))) == NULL ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_MEM ); + } + adp->ad_source = adsource; + adp->ad_attrname = toks[ 1 ]; + if ( adsource == LDAP_ADSRC_CONSTANTVALUE ) { + adp->ad_value = toks[ 2 ]; + } + NSLDAPI_FREE( toks[ 0 ] ); + NSLDAPI_FREE( (char *)toks ); + + if ( tmpl->dt_adddeflist == NULL ) { + tmpl->dt_adddeflist = adp; + } else { + prevadp->ad_next = adp; + } + prevadp = adp; + } + + /* + * item list is next + */ + samerow = 0; + while (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) > 0 ) { + if ( strcasecmp( toks[ 0 ], "item" ) == 0 ) { + if ( tokcnt < 4 ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + + if (( ip = (struct ldap_tmplitem *)NSLDAPI_CALLOC( 1, + sizeof( struct ldap_tmplitem ))) == NULL ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_MEM ); + } + + /* + * find syntaxid from config file string + */ + while (( itemopts = strrchr( toks[ 1 ], ',' )) != NULL ) { + *itemopts++ = '\0'; + for ( i = 0; itemoptions[ i ] != NULL; ++i ) { + if ( strcasecmp( itemopts, itemoptions[ i ] ) == 0 ) { + break; + } + } + if ( itemoptions[ i ] == NULL ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + ip->ti_options |= itemoptvals[ i ]; + } + + for ( i = 0; itemtypes[ i ] != NULL; ++i ) { + if ( strcasecmp( toks[ 1 ], itemtypes[ i ] ) == 0 ) { + break; + } + } + if ( itemtypes[ i ] == NULL ) { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + + NSLDAPI_FREE( toks[ 0 ] ); + NSLDAPI_FREE( toks[ 1 ] ); + ip->ti_syntaxid = itemsynids[ i ]; + ip->ti_label = toks[ 2 ]; + if ( toks[ 3 ][ 0 ] == '\0' ) { + ip->ti_attrname = NULL; + NSLDAPI_FREE( toks[ 3 ] ); + } else { + ip->ti_attrname = toks[ 3 ]; + } + if ( toks[ 4 ] != NULL ) { /* extra args. */ + for ( i = 0; toks[ i + 4 ] != NULL; ++i ) { + ; + } + if (( ip->ti_args = (char **)NSLDAPI_CALLOC( i + 1, + sizeof( char * ))) == NULL ) { + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_MEM ); + } + for ( i = 0; toks[ i + 4 ] != NULL; ++i ) { + ip->ti_args[ i ] = toks[ i + 4 ]; + } + } + NSLDAPI_FREE( (char *)toks ); + + if ( tmpl->dt_items == NULL ) { + tmpl->dt_items = rowp = ip; + } else if ( samerow ) { + previp->ti_next_in_row = ip; + } else { + rowp->ti_next_in_col = ip; + rowp = ip; + } + previp = ip; + samerow = 0; + } else if ( strcasecmp( toks[ 0 ], "samerow" ) == 0 ) { + nsldapi_free_strarray( toks ); + samerow = 1; + } else { + nsldapi_free_strarray( toks ); + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + } + if ( tokcnt < 0 ) { + free_disptmpl( tmpl ); + return( LDAP_TMPL_ERR_SYNTAX ); + } + + *tmplp = tmpl; + return( 0 ); +} + + +struct tmplerror { + int e_code; + char *e_reason; +}; + +static struct tmplerror ldap_tmplerrlist[] = { + { LDAP_TMPL_ERR_VERSION, "Bad template version" }, + { LDAP_TMPL_ERR_MEM, "Out of memory" }, + { LDAP_TMPL_ERR_SYNTAX, "Bad template syntax" }, + { LDAP_TMPL_ERR_FILE, "File error reading template" }, + { -1, 0 } +}; + +char * +LDAP_CALL +ldap_tmplerr2string( int err ) +{ + int i; + + for ( i = 0; ldap_tmplerrlist[i].e_code != -1; i++ ) { + if ( err == ldap_tmplerrlist[i].e_code ) + return( ldap_tmplerrlist[i].e_reason ); + } + + return( "Unknown error" ); +} diff --git a/ldap/c-sdk/libraries/libldap/dllmain.c b/ldap/c-sdk/libraries/libldap/dllmain.c new file mode 100644 index 0000000000..b51c16caa4 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/dllmain.c @@ -0,0 +1,178 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Microsoft Windows specifics for LIBLDAP DLL + */ +#include "ldap.h" +#include "lber.h" + + +#ifdef _WIN32 +/* Lifted from Q125688 + * How to Port a 16-bit DLL to a Win32 DLL + * on the MSVC 4.0 CD + */ +BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + /* Code from LibMain inserted here. Return TRUE to keep the + DLL loaded or return FALSE to fail loading the DLL. + + You may have to modify the code in your original LibMain to + account for the fact that it may be called more than once. + You will get one DLL_PROCESS_ATTACH for each process that + loads the DLL. This is different from LibMain which gets + called only once when the DLL is loaded. The only time this + is critical is when you are using shared data sections. + If you are using shared data sections for statically + allocated data, you will need to be careful to initialize it + only once. Check your code carefully. + + Certain one-time initializations may now need to be done for + each process that attaches. You may also not need code from + your original LibMain because the operating system may now + be doing it for you. + */ + /* + * 16 bit code calls UnlockData() + * which is mapped to UnlockSegment in windows.h + * in 32 bit world UnlockData is not defined anywhere + * UnlockSegment is mapped to GlobalUnfix in winbase.h + * and the docs for both UnlockSegment and GlobalUnfix say + * ".. function is oboslete. Segments have no meaning + * in the 32-bit environment". So we do nothing here. + */ + /* If we are building a version that includes the security libraries, + * we have to initialize Winsock here. If not, we can defer until the + * first real socket call is made (in mozock.c). + */ +#ifdef LINK_SSL + { + WSADATA wsaData; + WSAStartup(0x0101, &wsaData); + } +#endif + + break; + + case DLL_THREAD_ATTACH: + /* Called each time a thread is created in a process that has + already loaded (attached to) this DLL. Does not get called + for each thread that exists in the process before it loaded + the DLL. + + Do thread-specific initialization here. + */ + break; + + case DLL_THREAD_DETACH: + /* Same as above, but called when a thread in the process + exits. + + Do thread-specific cleanup here. + */ + break; + + case DLL_PROCESS_DETACH: + /* Code from _WEP inserted here. This code may (like the + LibMain) not be necessary. Check to make certain that the + operating system is not doing it for you. + */ +#ifdef LINK_SSL + WSACleanup(); +#endif + + break; + } + /* The return value is only used for DLL_PROCESS_ATTACH; all other + conditions are ignored. */ + return TRUE; // successful DLL_PROCESS_ATTACH +} +#else +int CALLBACK +LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + /*UnlockData( 0 );*/ + return( 1 ); +} + +BOOL CALLBACK __loadds WEP(BOOL fSystemExit) +{ + WSACleanup(); + return TRUE; +} + +#endif + +#ifdef LDAP_DEBUG +#ifndef _WIN32 +#include +#include + +void LDAP_C LDAPDebug( int level, char* fmt, ... ) +{ + static char debugBuf[1024]; + + if (ldap_debug & level) + { + va_list ap; + va_start (ap, fmt); + _snprintf (debugBuf, sizeof(debugBuf), fmt, ap); + va_end (ap); + + OutputDebugString (debugBuf); + } +} +#endif +#endif + +#ifndef _WIN32 + +/* The 16-bit version of the RTL does not implement perror() */ + +#include + +void perror( const char *msg ) +{ + char buf[128]; + wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ; + OutputDebugString( buf ); +} + +#endif diff --git a/ldap/c-sdk/libraries/libldap/dsparse.c b/ldap/c-sdk/libraries/libldap/dsparse.c new file mode 100644 index 0000000000..22d411617c --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/dsparse.c @@ -0,0 +1,227 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* + * dsparse.c: parsing routines used by display template and search + * preference file library routines for LDAP clients. + * + */ + +#include "ldap-int.h" + +static int next_line( char **bufp, long *blenp, char **linep ); +static char *next_token( char ** sp ); + +int +nsldapi_next_line_tokens( char **bufp, long *blenp, char ***toksp ) +{ + char *p, *line, *token, **toks; + int rc, tokcnt; + + *toksp = NULL; + + if (( rc = next_line( bufp, blenp, &line )) <= 0 ) { + return( rc ); + } + + if (( toks = (char **)NSLDAPI_CALLOC( 1, sizeof( char * ))) == NULL ) { + NSLDAPI_FREE( line ); + return( -1 ); + } + tokcnt = 0; + + p = line; + while (( token = next_token( &p )) != NULL ) { + if (( toks = (char **)NSLDAPI_REALLOC( toks, ( tokcnt + 2 ) * + sizeof( char * ))) == NULL ) { + NSLDAPI_FREE( (char *)toks ); + NSLDAPI_FREE( line ); + return( -1 ); + } + toks[ tokcnt ] = token; + toks[ ++tokcnt ] = NULL; + } + + if ( tokcnt == 1 && strcasecmp( toks[ 0 ], "END" ) == 0 ) { + tokcnt = 0; + nsldapi_free_strarray( toks ); + toks = NULL; + } + + NSLDAPI_FREE( line ); + + if ( tokcnt == 0 ) { + if ( toks != NULL ) { + NSLDAPI_FREE( (char *)toks ); + } + } else { + *toksp = toks; + } + + return( tokcnt ); +} + + +static int +next_line( char **bufp, long *blenp, char **linep ) +{ + char *linestart, *line, *p; + long plen; + + linestart = *bufp; + p = *bufp; + plen = *blenp; + + do { + for ( linestart = p; plen > 0; ++p, --plen ) { + if ( *p == '\r' ) { + if ( plen > 1 && *(p+1) == '\n' ) { + ++p; + --plen; + } + break; + } + + if ( *p == '\n' ) { + if ( plen > 1 && *(p+1) == '\r' ) { + ++p; + --plen; + } + break; + } + } + ++p; + --plen; + } while ( plen > 0 && ( *linestart == '#' || linestart + 1 == p )); + + + *bufp = p; + *blenp = plen; + + + if ( plen <= 0 ) { + *linep = NULL; + return( 0 ); /* end of file */ + } + + if (( line = NSLDAPI_MALLOC( p - linestart )) == NULL ) { + *linep = NULL; + return( -1 ); /* fatal error */ + } + + SAFEMEMCPY( line, linestart, p - linestart ); + line[ p - linestart - 1 ] = '\0'; + *linep = line; + return( strlen( line )); +} + + +static char * +next_token( char **sp ) +{ + int in_quote = 0; + char *p, *tokstart, *t; + + if ( **sp == '\0' ) { + return( NULL ); + } + + p = *sp; + + while ( ldap_utf8isspace( p )) { /* skip leading white space */ + ++p; + } + + if ( *p == '\0' ) { + return( NULL ); + } + + if ( *p == '\"' ) { + in_quote = 1; + ++p; + } + t = tokstart = p; + + for ( ;; ) { + if ( *p == '\0' || ( ldap_utf8isspace( p ) && !in_quote )) { + if ( *p != '\0' ) { + ++p; + } + *t++ = '\0'; /* end of token */ + break; + } + + if ( *p == '\"' ) { + in_quote = !in_quote; + ++p; + } else { + *t++ = *p++; + } + } + + *sp = p; + + if ( t == tokstart ) { + return( NULL ); + } + + return( nsldapi_strdup( tokstart )); +} + + +void +nsldapi_free_strarray( char **sap ) +{ + int i; + + if ( sap != NULL ) { + for ( i = 0; sap[ i ] != NULL; ++i ) { + NSLDAPI_FREE( sap[ i ] ); + } + NSLDAPI_FREE( (char *)sap ); + } +} diff --git a/ldap/c-sdk/libraries/libldap/error.c b/ldap/c-sdk/libraries/libldap/error.c new file mode 100644 index 0000000000..82cbb56cb7 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/error.c @@ -0,0 +1,490 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +struct ldaperror { + int e_code; + char *e_reason; +}; + +static struct ldaperror ldap_errlist[] = { + { LDAP_SUCCESS, "Success" }, + { LDAP_OPERATIONS_ERROR, "Operations error" }, + { LDAP_PROTOCOL_ERROR, "Protocol error" }, + { LDAP_TIMELIMIT_EXCEEDED, "Timelimit exceeded" }, + { LDAP_SIZELIMIT_EXCEEDED, "Sizelimit exceeded" }, + { LDAP_COMPARE_FALSE, "Compare false" }, + { LDAP_COMPARE_TRUE, "Compare true" }, + { LDAP_STRONG_AUTH_NOT_SUPPORTED, "Authentication method not supported" }, + { LDAP_STRONG_AUTH_REQUIRED, "Strong authentication required" }, + { LDAP_PARTIAL_RESULTS, "Partial results and referral received" }, + { LDAP_REFERRAL, "Referral received" }, + { LDAP_ADMINLIMIT_EXCEEDED, "Administrative limit exceeded" }, + { LDAP_UNAVAILABLE_CRITICAL_EXTENSION, "Unavailable critical extension" }, + { LDAP_CONFIDENTIALITY_REQUIRED, "Confidentiality required" }, + { LDAP_SASL_BIND_IN_PROGRESS, "SASL bind in progress" }, + + { LDAP_NO_SUCH_ATTRIBUTE, "No such attribute" }, + { LDAP_UNDEFINED_TYPE, "Undefined attribute type" }, + { LDAP_INAPPROPRIATE_MATCHING, "Inappropriate matching" }, + { LDAP_CONSTRAINT_VIOLATION, "Constraint violation" }, + { LDAP_TYPE_OR_VALUE_EXISTS, "Type or value exists" }, + { LDAP_INVALID_SYNTAX, "Invalid syntax" }, + + { LDAP_NO_SUCH_OBJECT, "No such object" }, + { LDAP_ALIAS_PROBLEM, "Alias problem" }, + { LDAP_INVALID_DN_SYNTAX, "Invalid DN syntax" }, + { LDAP_IS_LEAF, "Object is a leaf" }, + { LDAP_ALIAS_DEREF_PROBLEM, "Alias dereferencing problem" }, + + { LDAP_INAPPROPRIATE_AUTH, "Inappropriate authentication" }, + { LDAP_INVALID_CREDENTIALS, "Invalid credentials" }, + { LDAP_INSUFFICIENT_ACCESS, "Insufficient access" }, + { LDAP_BUSY, "DSA is busy" }, + { LDAP_UNAVAILABLE, "DSA is unavailable" }, + { LDAP_UNWILLING_TO_PERFORM, "DSA is unwilling to perform" }, + { LDAP_LOOP_DETECT, "Loop detected" }, + { LDAP_SORT_CONTROL_MISSING, "Sort Control is missing" }, + { LDAP_INDEX_RANGE_ERROR, "Search results exceed the range specified by the offsets" }, + + { LDAP_NAMING_VIOLATION, "Naming violation" }, + { LDAP_OBJECT_CLASS_VIOLATION, "Object class violation" }, + { LDAP_NOT_ALLOWED_ON_NONLEAF, "Operation not allowed on nonleaf" }, + { LDAP_NOT_ALLOWED_ON_RDN, "Operation not allowed on RDN" }, + { LDAP_ALREADY_EXISTS, "Already exists" }, + { LDAP_NO_OBJECT_CLASS_MODS, "Cannot modify object class" }, + { LDAP_RESULTS_TOO_LARGE, "Results too large" }, + { LDAP_AFFECTS_MULTIPLE_DSAS, "Affects multiple servers" }, + + { LDAP_OTHER, "Unknown error" }, + { LDAP_SERVER_DOWN, "Can't contact LDAP server" }, + { LDAP_LOCAL_ERROR, "Local error" }, + { LDAP_ENCODING_ERROR, "Encoding error" }, + { LDAP_DECODING_ERROR, "Decoding error" }, + { LDAP_TIMEOUT, "Timed out" }, + { LDAP_AUTH_UNKNOWN, "Unknown authentication method" }, + { LDAP_FILTER_ERROR, "Bad search filter" }, + { LDAP_USER_CANCELLED, "User cancelled operation" }, + { LDAP_PARAM_ERROR, "Bad parameter to an ldap routine" }, + { LDAP_NO_MEMORY, "Out of memory" }, + { LDAP_CONNECT_ERROR, "Can't connect to the LDAP server" }, + { LDAP_NOT_SUPPORTED, "Not supported by this version of the LDAP protocol" }, + { LDAP_CONTROL_NOT_FOUND, "Requested LDAP control not found" }, + { LDAP_NO_RESULTS_RETURNED, "No results returned" }, + { LDAP_MORE_RESULTS_TO_RETURN, "More results to return" }, + { LDAP_CLIENT_LOOP, "Client detected loop" }, + { LDAP_REFERRAL_LIMIT_EXCEEDED, "Referral hop limit exceeded" }, + { -1, 0 } +}; + +char * +LDAP_CALL +ldap_err2string( int err ) +{ + int i; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_err2string\n", 0, 0, 0 ); + + for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) { + if ( err == ldap_errlist[i].e_code ) + return( ldap_errlist[i].e_reason ); + } + + return( "Unknown error" ); +} + + +static char * +nsldapi_safe_strerror( int e ) +{ + char *s; + + if (( s = strerror( e )) == NULL ) { + s = "unknown error"; + } + + return( s ); +} + + +void +LDAP_CALL +ldap_perror( LDAP *ld, const char *s ) +{ + int i, err; + char *matched = NULL; + char *errmsg = NULL; + char *separator; + char msg[1024]; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_perror\n", 0, 0, 0 ); + + if ( s == NULL ) { + s = separator = ""; + } else { + separator = ": "; + } + + if ( ld == NULL ) { + snprintf( msg, sizeof( msg ), + "%s%s%s", s, separator, nsldapi_safe_strerror( errno ) ); + ber_err_print( msg ); + return; + } + + LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK ); + err = LDAP_GET_LDERRNO( ld, &matched, &errmsg ); + for ( i = 0; ldap_errlist[i].e_code != -1; i++ ) { + if ( err == ldap_errlist[i].e_code ) { + snprintf( msg, sizeof( msg ), + "%s%s%s", s, separator, ldap_errlist[i].e_reason ); + ber_err_print( msg ); + if ( err == LDAP_CONNECT_ERROR ) { + ber_err_print( " - " ); + ber_err_print( nsldapi_safe_strerror( + LDAP_GET_ERRNO( ld ))); + } + ber_err_print( "\n" ); + if ( matched != NULL && *matched != '\0' ) { + snprintf( msg, sizeof( msg ), + "%s%smatched: %s\n", s, separator, matched ); + ber_err_print( msg ); + } + if ( errmsg != NULL && *errmsg != '\0' ) { + snprintf( msg, sizeof( msg ), + "%s%sadditional info: %s\n", s, separator, errmsg ); + ber_err_print( msg ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK ); + return; + } + } + snprintf( msg, sizeof( msg ), + "%s%sNot an LDAP errno %d\n", s, separator, err ); + ber_err_print( msg ); + LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK ); +} + +int +LDAP_CALL +ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit ) +{ + int lderr_parse, lderr; + + lderr_parse = ldap_parse_result( ld, r, &lderr, NULL, NULL, NULL, + NULL, freeit ); + + if ( lderr_parse != LDAP_SUCCESS ) { + return( lderr_parse ); + } + + return( lderr ); +} + +int +LDAP_CALL +ldap_get_lderrno( LDAP *ld, char **m, char **s ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); /* punt */ + } + + if ( ld->ld_get_lderrno_fn == NULL ) { + if ( m != NULL ) { + *m = ld->ld_matched; + } + if ( s != NULL ) { + *s = ld->ld_error; + } + return( ld->ld_errno ); + } else { + return( ld->ld_get_lderrno_fn( m, s, ld->ld_lderrno_arg ) ); + } +} + + +/* + * Note: there is no need for callers of ldap_set_lderrno() to lock the + * ld mutex. If applications intend to share an LDAP session handle + * between threads they *must* perform their own locking around the + * session handle or they must install a "set lderrno" thread callback + * function. + * + */ +int +LDAP_CALL +ldap_set_lderrno( LDAP *ld, int e, char *m, char *s ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ld->ld_set_lderrno_fn != NULL ) { + ld->ld_set_lderrno_fn( e, m, s, ld->ld_lderrno_arg ); + } else { + LDAP_MUTEX_LOCK( ld, LDAP_ERR_LOCK ); + ld->ld_errno = e; + if ( ld->ld_matched ) { + NSLDAPI_FREE( ld->ld_matched ); + } + ld->ld_matched = m; + if ( ld->ld_error ) { + NSLDAPI_FREE( ld->ld_error ); + } + ld->ld_error = s; + LDAP_MUTEX_UNLOCK( ld, LDAP_ERR_LOCK ); + } + + return( LDAP_SUCCESS ); +} + + +/* + * Returns an LDAP error that says whether parse succeeded. The error code + * from the LDAP result itself is returned in the errcodep result parameter. + * If any of the result params. (errcodep, matchednp, errmsgp, referralsp, + * or serverctrlsp) are NULL we don't return that info. + */ +int +LDAP_CALL +ldap_parse_result( LDAP *ld, LDAPMessage *res, int *errcodep, char **matchednp, + char **errmsgp, char ***referralsp, LDAPControl ***serverctrlsp, + int freeit ) +{ + LDAPMessage *lm; + int err, errcode; + char *m, *e; + m = e = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || + !NSLDAPI_VALID_LDAPMESSAGE_POINTER( res )) { + return( LDAP_PARAM_ERROR ); + } + + /* skip over entries and references to find next result in this chain */ + for ( lm = res; lm != NULL; lm = lm->lm_chain ) { + if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY && + lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) { + break; + } + } + + if ( lm == NULL ) { + err = LDAP_NO_RESULTS_RETURNED; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + err = nsldapi_parse_result( ld, lm->lm_msgtype, lm->lm_ber, &errcode, + &m, &e, referralsp, serverctrlsp ); + + if ( err == LDAP_SUCCESS ) { + if ( errcodep != NULL ) { + *errcodep = errcode; + } + if ( matchednp != NULL ) { + *matchednp = nsldapi_strdup( m ); + } + if ( errmsgp != NULL ) { + *errmsgp = nsldapi_strdup( e ); + } + + /* + * if there are more result messages in the chain, arrange to + * return the special LDAP_MORE_RESULTS_TO_RETURN "error" code. + */ + for ( lm = lm->lm_chain; lm != NULL; lm = lm->lm_chain ) { + if ( lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY && + lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) { + err = LDAP_MORE_RESULTS_TO_RETURN; + break; + } + } + } else { + /* In this case, m and e were already freed by ber_scanf */ + m = e = NULL; + } + + if ( freeit ) { + ldap_msgfree( res ); + } + + LDAP_SET_LDERRNO( ld, ( err == LDAP_SUCCESS ) ? errcode : err, m, e ); + + /* nsldapi_parse_result set m and e, so we have to delete them if they exist. + Only delete them if they haven't been reused for matchednp or errmsgp. + if ( err == LDAP_SUCCESS ) { + if ( (m != NULL) && (matchednp == NULL) ) { + NSLDAPI_FREE( m ); + } + if ( (e != NULL) && (errmsgp == NULL) ) { + NSLDAPI_FREE( e ); + } + } */ + + return( err ); +} + +/* + * returns an LDAP error code indicating success or failure of parsing + * does NOT set any error information inside "ld" + */ +int +nsldapi_parse_result( LDAP *ld, int msgtype, BerElement *rber, int *errcodep, + char **matchednp, char **errmsgp, char ***referralsp, + LDAPControl ***serverctrlsp ) +{ + BerElement ber; + ber_len_t len; + ber_int_t errcode; + int berrc, err; + char *m, *e; + + /* + * Parse the result message. LDAPv3 result messages look like this: + * + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED { ... }, + * matchedDN LDAPDN, + * errorMessage LDAPString, + * referral [3] Referral OPTIONAL + * opSpecificStuff OPTIONAL + * } + * + * all wrapped up in an LDAPMessage sequence which looks like this: + * LDAPMessage ::= SEQUENCE { + * messageID MessageID, + * LDAPResult CHOICE { ... }, // message type + * controls [0] Controls OPTIONAL + * } + * + * LDAPv2 messages don't include referrals or controls. + * LDAPv1 messages don't include matchedDN, referrals, or controls. + * + * ldap_result() pulls out the message id, so by the time a result + * message gets here we are sitting at the start of the LDAPResult. + */ + + err = LDAP_SUCCESS; /* optimistic */ + m = e = NULL; + if ( matchednp != NULL ) { + *matchednp = NULL; + } + if ( errmsgp != NULL ) { + *errmsgp = NULL; + } + if ( referralsp != NULL ) { + *referralsp = NULL; + } + if ( serverctrlsp != NULL ) { + *serverctrlsp = NULL; + } + ber = *rber; /* struct copy */ + + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION2 ) { + berrc = ber_scanf( &ber, "{ia}", &errcode, &e ); + } else { + if (( berrc = ber_scanf( &ber, "{iaa", &errcode, &m, &e )) + != LBER_ERROR ) { + /* check for optional referrals */ + if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_REFERRAL ) { + if ( referralsp == NULL ) { + /* skip referrals */ + berrc = ber_scanf( &ber, "x" ); + } else { + /* suck out referrals */ + berrc = ber_scanf( &ber, "v", + referralsp ); + } + } else if ( referralsp != NULL ) { + *referralsp = NULL; + } + } + + if ( berrc != LBER_ERROR ) { + /* + * skip past optional operation-specific elements: + * bind results - serverSASLcreds + * extendedop results - OID plus value + */ + if ( msgtype == LDAP_RES_BIND ) { + if ( ber_peek_tag( &ber, &len ) == + LDAP_TAG_SASL_RES_CREDS ) { + berrc = ber_scanf( &ber, "x" ); + } + } else if ( msgtype == LDAP_RES_EXTENDED ) { + if ( ber_peek_tag( &ber, &len ) == + LDAP_TAG_EXOP_RES_OID ) { + berrc = ber_scanf( &ber, "x" ); + } + if ( berrc != LBER_ERROR && + ber_peek_tag( &ber, &len ) == + LDAP_TAG_EXOP_RES_VALUE ) { + berrc = ber_scanf( &ber, "x" ); + } + } + } + + /* pull out controls (if requested and any are present) */ + if ( berrc != LBER_ERROR && serverctrlsp != NULL && + ( berrc = ber_scanf( &ber, "}" )) != LBER_ERROR ) { + err = nsldapi_get_controls( &ber, serverctrlsp ); + } + } + + if ( berrc == LBER_ERROR && err == LDAP_SUCCESS ) { + err = LDAP_DECODING_ERROR; + } + + if ( errcodep != NULL ) { + *errcodep = errcode; + } + if ( matchednp != NULL ) { + *matchednp = m; + } else if ( m != NULL ) { + NSLDAPI_FREE( m ); + } + if ( errmsgp != NULL ) { + *errmsgp = e; + } else if ( e != NULL ) { + NSLDAPI_FREE( e ); + } + + return( err ); +} diff --git a/ldap/c-sdk/libraries/libldap/extendop.c b/ldap/c-sdk/libraries/libldap/extendop.c new file mode 100644 index 0000000000..f1a92afcd5 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/extendop.c @@ -0,0 +1,275 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +/* + * ldap_extended_operation - initiate an arbitrary ldapv3 extended operation. + * the oid and data of the extended operation are supplied. Returns an + * LDAP error code. + * + * Example: + * struct berval exdata; + * char *exoid; + * int err, msgid; + * ... fill in oid and data ... + * err = ldap_extended_operation( ld, exoid, &exdata, NULL, NULL, &msgid ); + */ + +int +LDAP_CALL +ldap_extended_operation( + LDAP *ld, + const char *exoid, + const struct berval *exdata, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp +) +{ + BerElement *ber; + int rc, msgid; + + /* + * the ldapv3 extended operation request looks like this: + * + * ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + * requestName LDAPOID, + * requestValue OCTET STRING + * } + * + * all wrapped up in an LDAPMessage sequence. + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + + /* only ldapv3 or higher can do extended operations */ + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + rc = LDAP_NOT_SUPPORTED; + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + } + + if ( msgidp == NULL || exoid == NULL || *exoid == '\0' ) { + rc = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + +#if 0 + if ( ld->ld_cache_on && ld->ld_cache_extendedop != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_extendedop)( ld, msgid, + LDAP_REQ_EXTENDED, exoid, cred )) != 0 ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( rc ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } +#endif + + /* create a message to send */ + if (( rc = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( rc ); + } + + /* fill it in */ + if ( exdata ) { + if ( ber_printf( ber, "{it{tsto}", msgid, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, exoid, LDAP_TAG_EXOP_REQ_VALUE, + exdata->bv_val, exdata->bv_len ) == -1 ) { + rc = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + ber_free( ber, 1 ); + return( rc ); + } + } else { /* some implementations are pretty strict on empty values */ + if ( ber_printf( ber, "{it{ts}", msgid, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, exoid ) == -1 ) { + rc = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + ber_free( ber, 1 ); + return( rc ); + } + } + + if (( rc = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( rc ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_EXTENDED, NULL, + ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + + +/* + * ldap_extended_operation_s - perform an arbitrary ldapv3 extended operation. + * the oid and data of the extended operation are supplied. LDAP_SUCCESS + * is returned upon success, the ldap error code otherwise. + * + * Example: + * struct berval exdata, exretval; + * char *exoid; + * int rc; + * ... fill in oid and data ... + * rc = ldap_extended_operation_s( ld, exoid, &exdata, &exretval ); + */ +int +LDAP_CALL +ldap_extended_operation_s( + LDAP *ld, + const char *requestoid, + const struct berval *requestdata, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + char **retoidp, + struct berval **retdatap +) +{ + int err, msgid; + LDAPMessage *result; + + if (( err = ldap_extended_operation( ld, requestoid, requestdata, + serverctrls, clientctrls, &msgid )) != LDAP_SUCCESS ) { + return( err ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) + == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if (( err = ldap_parse_extended_result( ld, result, retoidp, retdatap, + 0 )) != LDAP_SUCCESS ) { + ldap_msgfree( result ); + return( err ); + } + + return( ldap_result2error( ld, result, 1 ) ); +} + + +/* + * Pull the oid returned by the server and the data out of an extended + * operation result. Return an LDAP error code. + */ +int +LDAP_CALL +ldap_parse_extended_result( + LDAP *ld, + LDAPMessage *res, + char **retoidp, /* may be NULL */ + struct berval **retdatap, /* may be NULL */ + int freeit +) +{ + struct berelement ber; + ber_len_t len; + ber_int_t err; + char *m, *e, *roid; + struct berval *rdata; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( !NSLDAPI_VALID_LDAPMESSAGE_EXRESULT_POINTER( res )) { + return( LDAP_PARAM_ERROR ); + } + + m = e = NULL; + ber = *(res->lm_ber); + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + if ( ber_scanf( &ber, "{iaa", &err, &m, &e ) == LBER_ERROR ) { + goto decoding_error; + } + roid = NULL; + if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_EXOP_RES_OID ) { + if ( ber_scanf( &ber, "a", &roid ) == LBER_ERROR ) { + goto decoding_error; + } + } + if ( retoidp != NULL ) { + *retoidp = roid; + } else if ( roid != NULL ) { + NSLDAPI_FREE( roid ); + } + + rdata = NULL; + if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_EXOP_RES_VALUE ) { + if ( ber_scanf( &ber, "O", &rdata ) == LBER_ERROR ) { + goto decoding_error; + } + } + if ( retdatap != NULL ) { + *retdatap = rdata; + } else if ( rdata != NULL ) { + ber_bvfree( rdata ); + } + + LDAP_SET_LDERRNO( ld, err, m, e ); + + if ( freeit ) { + ldap_msgfree( res ); + } + + return( LDAP_SUCCESS ); + +decoding_error:; + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( LDAP_DECODING_ERROR ); +} diff --git a/ldap/c-sdk/libraries/libldap/free.c b/ldap/c-sdk/libraries/libldap/free.c new file mode 100644 index 0000000000..821ebad10c --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/free.c @@ -0,0 +1,156 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1994 The Regents of the University of Michigan. + * All rights reserved. + */ +/* + * free.c - some free routines are included here to avoid having to + * link in lots of extra code when not using certain features + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1994 The Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +void +LDAP_CALL +ldap_getfilter_free( LDAPFiltDesc *lfdp ) +{ + LDAPFiltList *flp, *nextflp; + LDAPFiltInfo *fip, *nextfip; + + if ( lfdp == NULL ) { + return; + } + + for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = nextflp ) { + for ( fip = flp->lfl_ilist; fip != NULL; fip = nextfip ) { + nextfip = fip->lfi_next; + NSLDAPI_FREE( fip->lfi_filter ); + NSLDAPI_FREE( fip->lfi_desc ); + NSLDAPI_FREE( fip ); + } + nextflp = flp->lfl_next; + NSLDAPI_FREE( flp->lfl_pattern ); + NSLDAPI_FREE( flp->lfl_delims ); + NSLDAPI_FREE( flp->lfl_tag ); + NSLDAPI_FREE( flp ); + } + + if ( lfdp->lfd_curval != NULL ) { + NSLDAPI_FREE( lfdp->lfd_curval ); + } + if ( lfdp->lfd_curvalcopy != NULL ) { + NSLDAPI_FREE( lfdp->lfd_curvalcopy ); + } + if ( lfdp->lfd_curvalwords != NULL ) { + NSLDAPI_FREE( lfdp->lfd_curvalwords ); + } + if ( lfdp->lfd_filtprefix != NULL ) { + NSLDAPI_FREE( lfdp->lfd_filtprefix ); + } + if ( lfdp->lfd_filtsuffix != NULL ) { + NSLDAPI_FREE( lfdp->lfd_filtsuffix ); + } + + NSLDAPI_FREE( lfdp ); +} + + +/* + * free a null-terminated array of pointers to mod structures. the + * structures are freed, not the array itself, unless the freemods + * flag is set. + */ +void +LDAP_CALL +ldap_mods_free( LDAPMod **mods, int freemods ) +{ + int i; + + if ( !NSLDAPI_VALID_LDAPMOD_ARRAY( mods )) { + return; + } + + for ( i = 0; mods[i] != NULL; i++ ) { + if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) { + if ( mods[i]->mod_bvalues != NULL ) { + ber_bvecfree( mods[i]->mod_bvalues ); + } + } else if ( mods[i]->mod_values != NULL ) { + ldap_value_free( mods[i]->mod_values ); + } + if ( mods[i]->mod_type != NULL ) { + NSLDAPI_FREE( mods[i]->mod_type ); + } + NSLDAPI_FREE( (char *) mods[i] ); + } + + if ( freemods ) + NSLDAPI_FREE( (char *) mods ); +} + + +/* + * ldap_memfree() is needed to ensure that memory allocated by the C runtime + * assocated with libldap is freed by the same runtime code. + */ +void +LDAP_CALL +ldap_memfree( void *s ) +{ + if ( s != NULL ) { + NSLDAPI_FREE( s ); + } +} + + +/* + * ldap_ber_free() is just a cover for ber_free() + * ber_free() checks for ber == NULL, so we don't bother. + */ +void +LDAP_CALL +ldap_ber_free( BerElement *ber, int freebuf ) +{ + ber_free( ber, freebuf ); +} diff --git a/ldap/c-sdk/libraries/libldap/freevalues.c b/ldap/c-sdk/libraries/libldap/freevalues.c new file mode 100644 index 0000000000..29a0ea03f6 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/freevalues.c @@ -0,0 +1,73 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * freevalues.c + */ + +#include "ldap-int.h" + +void +LDAP_CALL +ldap_value_free( char **vals ) +{ + int i; + + if ( vals == NULL ) + return; + for ( i = 0; vals[i] != NULL; i++ ) + NSLDAPI_FREE( vals[i] ); + NSLDAPI_FREE( (char *) vals ); +} + +void +LDAP_CALL +ldap_value_free_len( struct berval **vals ) +{ + int i; + + if ( vals == NULL ) + return; + for ( i = 0; vals[i] != NULL; i++ ) { + NSLDAPI_FREE( vals[i]->bv_val ); + NSLDAPI_FREE( vals[i] ); + } + NSLDAPI_FREE( (char *) vals ); +} diff --git a/ldap/c-sdk/libraries/libldap/friendly.c b/ldap/c-sdk/libraries/libldap/friendly.c new file mode 100644 index 0000000000..d7b5dc65d7 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/friendly.c @@ -0,0 +1,151 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * friendly.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +char * +LDAP_CALL +ldap_friendly_name( char *filename, char *name, FriendlyMap *map ) +{ + int i, entries; + FILE *fp; + char *s; + char buf[BUFSIZ]; + + if ( map == NULL ) { + return( name ); + } + if ( NULL == name) + { + return (name); + } + + if ( *map == NULL ) { + if ( (fp = NSLDAPI_FOPEN( filename, "r" )) == NULL ) + return( name ); + + entries = 0; + while ( fgets( buf, sizeof(buf), fp ) != NULL ) { + if ( buf[0] != '#' ) + entries++; + } + rewind( fp ); + + if ( (*map = (FriendlyMap)NSLDAPI_MALLOC( (entries + 1) * + sizeof(struct friendly) )) == NULL ) { + fclose( fp ); + return( name ); + } + + i = 0; + while ( fgets( buf, sizeof(buf), fp ) != NULL && i < entries ) { + if ( buf[0] == '#' ) + continue; + + if ( (s = strchr( buf, '\n' )) != NULL ) + *s = '\0'; + + if ( (s = strchr( buf, '\t' )) == NULL ) + continue; + *s++ = '\0'; + + if ( *s == '"' ) { + int esc = 0, found = 0; + + for ( ++s; *s && !found; s++ ) { + switch ( *s ) { + case '\\': + esc = 1; + break; + case '"': + if ( !esc ) + found = 1; + /* FALL */ + default: + esc = 0; + break; + } + } + } + + (*map)[i].f_unfriendly = nsldapi_strdup( buf ); + (*map)[i].f_friendly = nsldapi_strdup( s ); + i++; + } + + fclose( fp ); + (*map)[i].f_unfriendly = NULL; + } + + for ( i = 0; (*map)[i].f_unfriendly != NULL; i++ ) { + if ( strcasecmp( name, (*map)[i].f_unfriendly ) == 0 ) + return( (*map)[i].f_friendly ); + } + return( name ); +} + + +void +LDAP_CALL +ldap_free_friendlymap( FriendlyMap *map ) +{ + struct friendly* pF; + + if ( map == NULL || *map == NULL ) { + return; + } + + for ( pF = *map; pF->f_unfriendly; pF++ ) { + NSLDAPI_FREE( pF->f_unfriendly ); + NSLDAPI_FREE( pF->f_friendly ); + } + NSLDAPI_FREE( *map ); + *map = NULL; +} diff --git a/ldap/c-sdk/libraries/libldap/getattr.c b/ldap/c-sdk/libraries/libldap/getattr.c new file mode 100644 index 0000000000..fbe688fea3 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getattr.c @@ -0,0 +1,150 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * getattr.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + + +static unsigned long +bytes_remaining( BerElement *ber ) +{ + ber_len_t len; + + if ( ber_get_option( ber, LBER_OPT_REMAINING_BYTES, &len ) != 0 ) { + return( 0 ); /* not sure what else to do.... */ + } + return( len ); +} + + +char * +LDAP_CALL +ldap_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **ber ) +{ + char *attr; + int err; + ber_len_t seqlength; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_first_attribute\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULL ); /* punt */ + } + + if ( ber == NULL || !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( NULL ); + } + + if ( nsldapi_alloc_ber_with_options( ld, ber ) != LDAP_SUCCESS ) { + return( NULL ); + } + + **ber = *entry->lm_ber; + + attr = NULL; /* pessimistic */ + err = LDAP_DECODING_ERROR; /* ditto */ + + /* + * Skip past the sequence, dn, and sequence of sequence. + * Reset number of bytes remaining so we confine the rest of our + * decoding to the current sequence. + */ + if ( ber_scanf( *ber, "{xl{", &seqlength ) != LBER_ERROR && + ber_set_option( *ber, LBER_OPT_REMAINING_BYTES, &seqlength ) + == 0 ) { + /* snarf the attribute type, and skip the set of values, + * leaving us positioned right before the next attribute + * type/value sequence. + */ + if ( ber_scanf( *ber, "{ax}", &attr ) != LBER_ERROR || + bytes_remaining( *ber ) == 0 ) { + err = LDAP_SUCCESS; + } + } + + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + if ( attr == NULL || err != LDAP_SUCCESS ) { + ber_free( *ber, 0 ); + *ber = NULL; + } + return( attr ); +} + +/* ARGSUSED */ +char * +LDAP_CALL +ldap_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber ) +{ + char *attr; + int err; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_next_attribute\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULL ); /* punt */ + } + + if ( ber == NULL || !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( NULL ); + } + + attr = NULL; /* pessimistic */ + err = LDAP_DECODING_ERROR; /* ditto */ + + /* skip sequence, snarf attribute type, skip values */ + if ( ber_scanf( ber, "{ax}", &attr ) != LBER_ERROR || + bytes_remaining( ber ) == 0 ) { + err = LDAP_SUCCESS; + } + + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( attr ); +} diff --git a/ldap/c-sdk/libraries/libldap/getdn.c b/ldap/c-sdk/libraries/libldap/getdn.c new file mode 100644 index 0000000000..4fab52cc6d --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getdn.c @@ -0,0 +1,371 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * getdn.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +char * +LDAP_CALL +ldap_get_dn( LDAP *ld, LDAPMessage *entry ) +{ + char *dn; + struct berelement tmp; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULL ); /* punt */ + } + + if ( !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( NULL ); + } + + tmp = *entry->lm_ber; /* struct copy */ + if ( ber_scanf( &tmp, "{a", &dn ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( NULL ); + } + + return( dn ); +} + +char * +LDAP_CALL +ldap_dn2ufn( const char *dn ) +{ + char *p, *ufn, *r; + size_t plen; + int state; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 ); + + if ( dn == NULL ) { + dn = ""; + } + + if ( ldap_is_dns_dn( dn ) || ( p = strchr( dn, '=' )) == NULL ) + return( nsldapi_strdup( (char *)dn )); + + ufn = nsldapi_strdup( ++p ); + +#define INQUOTE 1 +#define OUTQUOTE 2 + state = OUTQUOTE; + for ( p = ufn, r = ufn; *p; p += plen ) { + plen = 1; + switch ( *p ) { + case '\\': + if ( *++p == '\0' ) + plen=0; + else { + *r++ = '\\'; + r += (plen = LDAP_UTF8COPY(r,p)); + } + break; + case '"': + if ( state == INQUOTE ) + state = OUTQUOTE; + else + state = INQUOTE; + *r++ = *p; + break; + case ';': + case ',': + if ( state == OUTQUOTE ) + *r++ = ','; + else + *r++ = *p; + break; + case '=': + if ( state == INQUOTE ) + *r++ = *p; + else { + char *rsave = r; + LDAP_UTF8DEC(r); + *rsave = '\0'; + while ( !ldap_utf8isspace( r ) && *r != ';' + && *r != ',' && r > ufn ) + LDAP_UTF8DEC(r); + LDAP_UTF8INC(r); + + if ( strcasecmp( r, "c" ) + && strcasecmp( r, "o" ) + && strcasecmp( r, "ou" ) + && strcasecmp( r, "st" ) + && strcasecmp( r, "l" ) + && strcasecmp( r, "dc" ) + && strcasecmp( r, "uid" ) + && strcasecmp( r, "cn" ) ) { + r = rsave; + *r++ = '='; + } + } + break; + default: + r += (plen = LDAP_UTF8COPY(r,p)); + break; + } + } + *r = '\0'; + + return( ufn ); +} + +char ** +LDAP_CALL +ldap_explode_dns( const char *dn ) +{ + int ncomps, maxcomps; + char *s, *cpydn; + char **rdns; +#ifdef HAVE_STRTOK_R /* defined in portable.h */ + char *lasts; +#endif + + if ( dn == NULL ) { + dn = ""; + } + + if ( (rdns = (char **)NSLDAPI_MALLOC( 8 * sizeof(char *) )) == NULL ) { + return( NULL ); + } + + maxcomps = 8; + ncomps = 0; + cpydn = nsldapi_strdup( (char *)dn ); + for ( s = STRTOK( cpydn, "@.", &lasts ); s != NULL; + s = STRTOK( NULL, "@.", &lasts ) ) { + if ( ncomps == maxcomps ) { + maxcomps *= 2; + if ( (rdns = (char **)NSLDAPI_REALLOC( rdns, maxcomps * + sizeof(char *) )) == NULL ) { + NSLDAPI_FREE( cpydn ); + return( NULL ); + } + } + rdns[ncomps++] = nsldapi_strdup( s ); + } + rdns[ncomps] = NULL; + NSLDAPI_FREE( cpydn ); + + return( rdns ); +} + +#define LDAP_DN 1 +#define LDAP_RDN 2 + +static char ** +ldap_explode( const char *dn, const int notypes, const int nametype ) +{ + char *p, *q, *rdnstart, **rdns = NULL; + size_t plen = 0; + int state = 0; + int count = 0; + int startquote = 0; + int endquote = 0; + int len = 0; + int goteq = 0; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_explode\n", 0, 0, 0 ); + + if ( dn == NULL ) { + dn = ""; + } + +#if 0 + if ( ldap_is_dns_dn( dn ) ) { + return( ldap_explode_dns( dn ) ); + } +#endif + + while ( ldap_utf8isspace( (char *)dn )) { /* ignore leading spaces */ + ++dn; + } + + p = rdnstart = (char *) dn; + state = OUTQUOTE; + + do { + p += plen; + plen = 1; + switch ( *p ) { + case '\\': + if ( *++p == '\0' ) + p--; + else + plen = LDAP_UTF8LEN(p); + break; + case '"': + if ( state == INQUOTE ) + state = OUTQUOTE; + else + state = INQUOTE; + break; + case '+': if ( nametype != LDAP_RDN ) break; + case ';': + case ',': + case '\0': + if ( state == OUTQUOTE ) { + /* + * semicolon and comma are not valid RDN + * separators. + */ + if ( nametype == LDAP_RDN && + ( *p == ';' || *p == ',' || !goteq)) { + ldap_charray_free( rdns ); + return NULL; + } + if ( (*p == ',' || *p == ';') && !goteq ) { + /* If we get here, we have a case similar + * to =,,= + * This is not a valid dn */ + ldap_charray_free( rdns ); + return NULL; + } + goteq = 0; + ++count; + if ( rdns == NULL ) { + if (( rdns = (char **)NSLDAPI_MALLOC( 8 + * sizeof( char *))) == NULL ) + return( NULL ); + } else if ( count >= 8 ) { + if (( rdns = (char **)NSLDAPI_REALLOC( + rdns, (count+1) * + sizeof( char *))) == NULL ) + return( NULL ); + } + rdns[ count ] = NULL; + endquote = 0; + if ( notypes ) { + for ( q = rdnstart; + q < p && *q != '='; ++q ) { + ; + } + if ( q < p ) { /* *q == '=' */ + rdnstart = ++q; + } + if ( *rdnstart == '"' ) { + startquote = 1; + ++rdnstart; + } + + if ( (*(p-1) == '"') && startquote ) { + endquote = 1; + --p; + } + } + + len = p - rdnstart; + if (( rdns[ count-1 ] = (char *)NSLDAPI_CALLOC( + 1, len + 1 )) != NULL ) { + SAFEMEMCPY( rdns[ count-1 ], rdnstart, + len ); + if ( !endquote ) { + /* trim trailing spaces unless + * they are properly escaped */ + while ( len > 0 && + ldap_utf8isspace( + &rdns[count-1][len-1] ) && + ((len == 1) || (rdns[count-1][len-2] != '\\'))) { + --len; + } + } + rdns[ count-1 ][ len ] = '\0'; + } + + /* + * Don't forget to increment 'p' back to where + * it should be. If we don't, then we will + * never get past an "end quote." + */ + if ( endquote == 1 ) + p++; + + rdnstart = *p ? p + 1 : p; + while ( ldap_utf8isspace( rdnstart )) + ++rdnstart; + } + break; + case '=': + if ( state == OUTQUOTE ) { + goteq = 1; + } + /* FALL */ + default: + plen = LDAP_UTF8LEN(p); + break; + } + } while ( *p ); + + return( rdns ); +} + +char ** +LDAP_CALL +ldap_explode_dn( const char *dn, const int notypes ) +{ + return( ldap_explode( dn, notypes, LDAP_DN ) ); +} + +char ** +LDAP_CALL +ldap_explode_rdn( const char *rdn, const int notypes ) +{ + return( ldap_explode( rdn, notypes, LDAP_RDN ) ); +} + +int +LDAP_CALL +ldap_is_dns_dn( const char *dn ) +{ + return( dn != NULL && dn[ 0 ] != '\0' && strchr( dn, '=' ) == NULL && + strchr( dn, ',' ) == NULL ); +} diff --git a/ldap/c-sdk/libraries/libldap/getdxbyname.c b/ldap/c-sdk/libraries/libldap/getdxbyname.c new file mode 100644 index 0000000000..d706144586 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getdxbyname.c @@ -0,0 +1,279 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * nsldapi_getdxbyname - retrieve DX records from the DNS (from + * TXT records for now) + */ + +#include + +#ifdef LDAP_DNS + +XXX not MT-safe XXX + +#include +#include + +#ifdef macintosh +#include +#include "macos.h" +#endif /* macintosh */ + +#ifdef _WINDOWS +#include +#endif + +#if !defined(macintosh) && !defined(DOS) && !defined( _WINDOWS ) +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include "ldap-int.h" + +#if defined( DOS ) +#include "msdos.h" +#endif /* DOS */ + + +#ifdef NEEDPROTOS +static char ** decode_answer( unsigned char *answer, int len ); +#else /* NEEDPROTOS */ +static char **decode_answer(); +#endif /* NEEDPROTOS */ + +extern int h_errno; +extern char *h_errlist[]; + + +#define MAX_TO_SORT 32 + + +/* + * nsldapi_getdxbyname - lookup DNS DX records for domain and return an ordered + * array. + */ +char ** +nsldapi_getdxbyname( char *domain ) +{ + unsigned char buf[ PACKETSZ ]; + char **dxs; + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_getdxbyname( %s )\n", domain, 0, 0 ); + + memset( buf, 0, sizeof( buf )); + + /* XXX not MT safe XXX */ + if (( rc = res_search( domain, C_IN, T_TXT, buf, sizeof( buf ))) < 0 + || ( rc > sizeof( buf )) + || ( dxs = decode_answer( buf, rc )) == NULL ) { + /* + * punt: return list consisting of the original domain name only + */ + if (( dxs = (char **)NSLDAPI_MALLOC( 2 * sizeof( char * ))) == NULL || + ( dxs[ 0 ] = nsldapi_strdup( domain )) == NULL ) { + if ( dxs != NULL ) { + NSLDAPI_FREE( dxs ); + } + dxs = NULL; + } else { + dxs[ 1 ] = NULL; + } + } + + return( dxs ); +} + + +static char ** +decode_answer( unsigned char *answer, int len ) +{ + HEADER *hp; + char buf[ 256 ], **dxs; + unsigned char *eom, *p; + int ancount, err, rc, type, class, dx_count, rr_len; + int dx_pref[ MAX_TO_SORT ]; + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { +/* __p_query( answer ); */ + } +#endif /* LDAP_DEBUG */ + + dxs = NULL; + hp = (HEADER *)answer; + eom = answer + len; + + if ( len < sizeof( *hp ) ) { + h_errno = NO_RECOVERY; + return( NULL ); + } + + if ( ntohs( hp->qdcount ) != 1 ) { + h_errno = NO_RECOVERY; + return( NULL ); + } + + ancount = ntohs( hp->ancount ); + if ( ancount < 1 ) { + h_errno = NO_DATA; + return( NULL ); + } + + /* + * skip over the query + */ + p = answer + HFIXEDSZ; + if (( rc = dn_expand( answer, eom, p, buf, sizeof( buf ))) < 0 ) { + h_errno = NO_RECOVERY; + return( NULL ); + } + p += ( rc + QFIXEDSZ ); + + /* + * pull out the answers we are interested in + */ + err = dx_count = 0; + while ( ancount > 0 && err == 0 && p < eom ) { + if (( rc = dn_expand( answer, eom, p, buf, sizeof( buf ))) < 0 ) { + err = NO_RECOVERY; + continue; + } + p += rc; /* skip over name */ + if ( p + 3 * INT16SZ + INT32SZ > eom ) { + err = NO_RECOVERY; + continue; + } + type = _getshort( p ); + p += INT16SZ; + class = _getshort( p ); + p += INT16SZ; + p += INT32SZ; /* skip over TTL */ + rr_len = _getshort( p ); + p += INT16SZ; + if ( p + rr_len > eom ) { + err = NO_RECOVERY; + continue; + } + if ( class == C_IN && type == T_TXT ) { + int i, n, pref, txt_len; + char *q, *r; + + q = (char *)p; + while ( q < (char *)p + rr_len && err == 0 ) { + if ( *q >= 3 && strncasecmp( q + 1, "dx:", 3 ) == 0 ) { + txt_len = *q - 3; + r = q + 4; + while ( isspace( *r )) { + ++r; + --txt_len; + } + pref = 0; + while ( isdigit( *r )) { + pref *= 10; + pref += ( *r - '0' ); + ++r; + --txt_len; + } + if ( dx_count < MAX_TO_SORT - 1 ) { + dx_pref[ dx_count ] = pref; + } + while ( isspace( *r )) { + ++r; + --txt_len; + } + if ( dx_count == 0 ) { + dxs = (char **)NSLDAPI_MALLOC( 2 * sizeof( char * )); + } else { + dxs = (char **)NSLDAPI_REALLOC( dxs, + ( dx_count + 2 ) * sizeof( char * )); + } + if ( dxs == NULL || ( dxs[ dx_count ] = + (char *)NSLDAPI_CALLOC( 1, txt_len + 1 )) + == NULL ) { + err = NO_RECOVERY; + continue; + } + SAFEMEMCPY( dxs[ dx_count ], r, txt_len ); + dxs[ ++dx_count ] = NULL; + } + q += ( *q + 1 ); /* move past last TXT record */ + } + } + p += rr_len; + } + + if ( err == 0 ) { + if ( dx_count == 0 ) { + err = NO_DATA; + } else { + /* + * sort records based on associated preference value + */ + int i, j, sort_count, tmp_pref; + char *tmp_dx; + + sort_count = ( dx_count < MAX_TO_SORT ) ? dx_count : MAX_TO_SORT; + for ( i = 0; i < sort_count; ++i ) { + for ( j = i + 1; j < sort_count; ++j ) { + if ( dx_pref[ i ] > dx_pref[ j ] ) { + tmp_pref = dx_pref[ i ]; + dx_pref[ i ] = dx_pref[ j ]; + dx_pref[ j ] = tmp_pref; + tmp_dx = dxs[ i ]; + dxs[ i ] = dxs[ j ]; + dxs[ j ] = tmp_dx; + } + } + } + } + } + + h_errno = err; + return( dxs ); +} + +#endif /* LDAP_DNS */ diff --git a/ldap/c-sdk/libraries/libldap/geteffectiverightsctrl.c b/ldap/c-sdk/libraries/libldap/geteffectiverightsctrl.c new file mode 100644 index 0000000000..f03fdc4416 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/geteffectiverightsctrl.c @@ -0,0 +1,109 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_create_geteffectiveRights_control + + Create Effective Rights control. + + Parameters are + + ld LDAP pointer to the desired connection + + authzid RFC2829 section 9, eg "dn:". + NULL or empty string means get bound user's rights, + just "dn:" means get anonymous user's rights. + + attrlist additional attributes for which rights info is + requrested. NULL means "just the ones returned + with the search operation". + + ctl_iscritical Indicates whether the control is critical of not. If + this field is non-zero, the operation will only be car- + ried out if the control is recognized by the server + and/or client + + ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_geteffectiveRights_control ( + LDAP *ld, + const char *authzid, + const char **attrlist, + const char ctl_iscritical, + LDAPControl **ctrlp +) +{ + BerElement *ber; + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + if (NULL == authzid) + { + authzid = ""; + } + + /* create a ber package to hold the controlValue */ + if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + if ( LBER_ERROR == ber_printf( ber, "{s{v}}", authzid, attrlist ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_GETEFFECTIVERIGHTS_REQUEST, ber, 1, + ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + +} + + diff --git a/ldap/c-sdk/libraries/libldap/getentry.c b/ldap/c-sdk/libraries/libldap/getentry.c new file mode 100644 index 0000000000..fb999265b9 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getentry.c @@ -0,0 +1,141 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * getentry.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +LDAPMessage * +LDAP_CALL +ldap_first_entry( LDAP *ld, LDAPMessage *chain ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || chain == NULLMSG ) { + return( NULLMSG ); + } + + if ( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + return( chain ); + } + + return( ldap_next_entry( ld, chain )); +} + + +LDAPMessage * +LDAP_CALL +ldap_next_entry( LDAP *ld, LDAPMessage *entry ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || entry == NULLMSG ) { + return( NULLMSG ); + } + + for ( entry = entry->lm_chain; entry != NULLMSG; + entry = entry->lm_chain ) { + if ( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + return( entry ); + } + } + + return( NULLMSG ); +} + +int +LDAP_CALL +ldap_count_entries( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); + } + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + if ( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + ++i; + } + } + + return( i ); +} + + +int +LDAP_CALL +ldap_get_entry_controls( LDAP *ld, LDAPMessage *entry, + LDAPControl ***serverctrlsp ) +{ + int rc; + BerElement tmpber; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_entry_controls\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry ) + || serverctrlsp == NULL ) { + rc = LDAP_PARAM_ERROR; + goto report_error_and_return; + } + + *serverctrlsp = NULL; + tmpber = *entry->lm_ber; /* struct copy */ + + /* skip past dn and entire attribute/value list */ + if ( ber_scanf( &tmpber, "{xx" ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto report_error_and_return; + } + + rc = nsldapi_get_controls( &tmpber, serverctrlsp ); + +report_error_and_return: + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libldap/getfilter.c b/ldap/c-sdk/libraries/libldap/getfilter.c new file mode 100644 index 0000000000..ff2ee77f91 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getfilter.c @@ -0,0 +1,553 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * getfilter.c -- optional add-on to libldap + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" +#include "regex.h" + +static int break_into_words( char *str, char *delims, char ***wordsp ); + +#if !defined( macintosh ) && !defined( DOS ) +extern char * LDAP_CALL re_comp(); +#endif + +#define FILT_MAX_LINE_LEN 1024 + +LDAPFiltDesc * +LDAP_CALL +ldap_init_getfilter( char *fname ) +{ + FILE *fp; + char *buf; + long rlen, len; + int eof; + LDAPFiltDesc *lfdp; + + if (( fp = NSLDAPI_FOPEN( fname, "r" )) == NULL ) { + return( NULL ); + } + + if ( fseek( fp, 0L, SEEK_END ) != 0 ) { /* move to end to get len */ + fclose( fp ); + return( NULL ); + } + + len = ftell( fp ); + + if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { /* back to start of file */ + fclose( fp ); + return( NULL ); + } + + if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) { + fclose( fp ); + return( NULL ); + } + + rlen = fread( buf, 1, (size_t)len, fp ); + eof = feof( fp ); + fclose( fp ); + + if ( rlen != len && !eof ) { /* error: didn't get the whole file */ + NSLDAPI_FREE( buf ); + return( NULL ); + } + + + lfdp = ldap_init_getfilter_buf( buf, rlen ); + NSLDAPI_FREE( buf ); + + return( lfdp ); +} + + +LDAPFiltDesc * +LDAP_CALL +ldap_init_getfilter_buf( char *buf, long buflen ) +{ + LDAPFiltDesc *lfdp; + LDAPFiltList *flp, *nextflp; + LDAPFiltInfo *fip, *nextfip; + char *errmsg, *tag, **tok; + int tokcnt, i; + + if ( (buf == NULL) || (buflen < 0) || + ( lfdp = (LDAPFiltDesc *)NSLDAPI_CALLOC(1, sizeof( LDAPFiltDesc))) + == NULL ) { + return( NULL ); + } + + flp = nextflp = NULL; + fip = NULL; + tag = NULL; + + while ( buflen > 0 && ( tokcnt = nsldapi_next_line_tokens( &buf, &buflen, + &tok )) > 0 ) { + switch( tokcnt ) { + case 1: /* tag line */ + if ( tag != NULL ) { + NSLDAPI_FREE( tag ); + } + tag = tok[ 0 ]; + NSLDAPI_FREE( tok ); + break; + case 4: + case 5: /* start of filter info. list */ + if (( nextflp = (LDAPFiltList *)NSLDAPI_CALLOC( 1, + sizeof( LDAPFiltList ))) == NULL ) { + ldap_getfilter_free( lfdp ); + return( NULL ); + } + nextflp->lfl_tag = nsldapi_strdup( tag ); + nextflp->lfl_pattern = tok[ 0 ]; + if (( errmsg = re_comp( nextflp->lfl_pattern )) != NULL ) { + char msg[512]; + ldap_getfilter_free( lfdp ); + snprintf( msg, sizeof(msg), + "bad regular expression \"%s\" - %s\n", + nextflp->lfl_pattern, errmsg ); + ber_err_print( msg ); + nsldapi_free_strarray( tok ); + return( NULL ); + } + + nextflp->lfl_delims = tok[ 1 ]; + nextflp->lfl_ilist = NULL; + nextflp->lfl_next = NULL; + if ( flp == NULL ) { /* first one */ + lfdp->lfd_filtlist = nextflp; + } else { + flp->lfl_next = nextflp; + } + flp = nextflp; + fip = NULL; + for ( i = 2; i < 5; ++i ) { + tok[ i - 2 ] = tok[ i ]; + } + /* fall through */ + + case 2: + case 3: /* filter, desc, and optional search scope */ + if ( nextflp != NULL ) { /* add to info list */ + if (( nextfip = (LDAPFiltInfo *)NSLDAPI_CALLOC( 1, + sizeof( LDAPFiltInfo ))) == NULL ) { + ldap_getfilter_free( lfdp ); + nsldapi_free_strarray( tok ); + return( NULL ); + } + if ( fip == NULL ) { /* first one */ + nextflp->lfl_ilist = nextfip; + } else { + fip->lfi_next = nextfip; + } + fip = nextfip; + nextfip->lfi_next = NULL; + nextfip->lfi_filter = tok[ 0 ]; + nextfip->lfi_desc = tok[ 1 ]; + if ( tok[ 2 ] != NULL ) { + if ( strcasecmp( tok[ 2 ], "subtree" ) == 0 ) { + nextfip->lfi_scope = LDAP_SCOPE_SUBTREE; + } else if ( strcasecmp( tok[ 2 ], "onelevel" ) == 0 ) { + nextfip->lfi_scope = LDAP_SCOPE_ONELEVEL; + } else if ( strcasecmp( tok[ 2 ], "base" ) == 0 ) { + nextfip->lfi_scope = LDAP_SCOPE_BASE; + } else { + nsldapi_free_strarray( tok ); + ldap_getfilter_free( lfdp ); + return( NULL ); + } + NSLDAPI_FREE( tok[ 2 ] ); + tok[ 2 ] = NULL; + } else { + nextfip->lfi_scope = LDAP_SCOPE_SUBTREE; /* default */ + } + nextfip->lfi_isexact = ( strchr( tok[ 0 ], '*' ) == NULL && + strchr( tok[ 0 ], '~' ) == NULL ); + NSLDAPI_FREE( tok ); + } + break; + + default: + nsldapi_free_strarray( tok ); + ldap_getfilter_free( lfdp ); + return( NULL ); + } + } + + if ( tag != NULL ) { + NSLDAPI_FREE( tag ); + } + + return( lfdp ); +} + + +int +LDAP_CALL +ldap_set_filter_additions( LDAPFiltDesc *lfdp, char *prefix, char *suffix ) +{ + if ( lfdp == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + if ( lfdp->lfd_filtprefix != NULL ) { + NSLDAPI_FREE( lfdp->lfd_filtprefix ); + } + lfdp->lfd_filtprefix = ( prefix == NULL ) ? NULL : nsldapi_strdup( prefix ); + + if ( lfdp->lfd_filtsuffix != NULL ) { + NSLDAPI_FREE( lfdp->lfd_filtsuffix ); + } + lfdp->lfd_filtsuffix = ( suffix == NULL ) ? NULL : nsldapi_strdup( suffix ); + + return( LDAP_SUCCESS ); +} + + +/* + * ldap_setfilteraffixes() is deprecated -- use ldap_set_filter_additions() + */ +void +LDAP_CALL +ldap_setfilteraffixes( LDAPFiltDesc *lfdp, char *prefix, char *suffix ) +{ + (void)ldap_set_filter_additions( lfdp, prefix, suffix ); +} + + +LDAPFiltInfo * +LDAP_CALL +ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat, char *value ) +{ + LDAPFiltList *flp; + + if ( lfdp == NULL || tagpat == NULL || value == NULL ) { + return( NULL ); /* punt */ + } + + if ( lfdp->lfd_curvalcopy != NULL ) { + NSLDAPI_FREE( lfdp->lfd_curvalcopy ); + NSLDAPI_FREE( lfdp->lfd_curvalwords ); + } + + NSLDAPI_FREE(lfdp->lfd_curval); + if ((lfdp->lfd_curval = nsldapi_strdup(value)) == NULL) { + return( NULL ); + } + + lfdp->lfd_curfip = NULL; + + for ( flp = lfdp->lfd_filtlist; flp != NULL; flp = flp->lfl_next ) { + if ( re_comp( tagpat ) == NULL && re_exec( flp->lfl_tag ) == 1 + && re_comp( flp->lfl_pattern ) == NULL + && re_exec( lfdp->lfd_curval ) == 1 ) { + lfdp->lfd_curfip = flp->lfl_ilist; + break; + } + } + + if ( lfdp->lfd_curfip == NULL ) { + return( NULL ); + } + + if (( lfdp->lfd_curvalcopy = nsldapi_strdup( value )) == NULL ) { + return( NULL ); + } + + if ( break_into_words( lfdp->lfd_curvalcopy, flp->lfl_delims, + &lfdp->lfd_curvalwords ) < 0 ) { + NSLDAPI_FREE( lfdp->lfd_curvalcopy ); + lfdp->lfd_curvalcopy = NULL; + return( NULL ); + } + + return( ldap_getnextfilter( lfdp )); +} + + +LDAPFiltInfo * +LDAP_CALL +ldap_getnextfilter( LDAPFiltDesc *lfdp ) +{ + LDAPFiltInfo *fip; + + if ( lfdp == NULL || ( fip = lfdp->lfd_curfip ) == NULL ) { + return( NULL ); + } + + lfdp->lfd_curfip = fip->lfi_next; + + ldap_build_filter( lfdp->lfd_filter, LDAP_FILT_MAXSIZ, fip->lfi_filter, + lfdp->lfd_filtprefix, lfdp->lfd_filtsuffix, NULL, + lfdp->lfd_curval, lfdp->lfd_curvalwords ); + lfdp->lfd_retfi.lfi_filter = lfdp->lfd_filter; + lfdp->lfd_retfi.lfi_desc = fip->lfi_desc; + lfdp->lfd_retfi.lfi_scope = fip->lfi_scope; + lfdp->lfd_retfi.lfi_isexact = fip->lfi_isexact; + + return( &lfdp->lfd_retfi ); +} + + +static char* +filter_add_strn( char *f, char *flimit, char *v, size_t vlen ) + /* Copy v into f. If flimit is too small, return NULL; + * otherwise return (f + vlen). + */ +{ + auto size_t flen = flimit - f; + if ( vlen > flen ) { /* flimit is too small */ + if ( flen > 0 ) SAFEMEMCPY( f, v, flen ); + return NULL; + } + if ( vlen > 0 ) SAFEMEMCPY( f, v, vlen ); + return f + vlen; +} + +static char* +filter_add_value( char *f, char *flimit, char *v, int escape_all ) + /* Copy v into f, but with parentheses escaped. But only escape * and \ + * if escape_all is non-zero so that either "*" or "\2a" can be used in + * v, with different meanings. + * If flimit is too small, return NULL; otherwise + * return (f + the number of bytes copied). + */ +{ + auto char x[4]; + auto size_t slen; + while ( f && *v ) { + switch ( *v ) { + case '*': + if ( escape_all ) { + f = filter_add_strn( f, flimit, "\\2a", 3 ); + v++; + } else { + if ( f < flimit ) { + *f++ = *v++; + } else { + f = NULL; /* overflow */ + } + } + break; + + case '(': + case ')': + sprintf( x, "\\%02x", (unsigned)*v ); + f = filter_add_strn( f, flimit, x, 3 ); + v++; + break; + + case '\\': + if ( escape_all ) { + f = filter_add_strn( f, flimit, "\\5c", 3 ); + v++; + } else { + slen = (ldap_utf8isxdigit( v+1 ) && + ldap_utf8isxdigit( v+2 )) ? 3 : (v[1] ? 2 : 1); + f = filter_add_strn( f, flimit, v, slen ); + v += slen; + } + break; + + default: + if ( f < flimit ) { + *f++ = *v++; + } else { + f = NULL; /* overflow */ + } + break; + } + } + return f; +} + +int +LDAP_CALL +ldap_create_filter( char *filtbuf, unsigned long buflen, char *pattern, + char *prefix, char *suffix, char *attr, char *value, char **valwords ) +{ + char *p, *f, *flimit; + int i, wordcount, wordnum, endwordnum, escape_all; + + /* + * there is some confusion on what to create for a filter if + * attr or value are null pointers. For now we just leave them + * as TO BE DEALT with + */ + + if ( filtbuf == NULL || buflen == 0 || pattern == NULL ){ + return( LDAP_PARAM_ERROR ); + } + + if ( valwords == NULL ) { + wordcount = 0; + } else { + for ( wordcount = 0; valwords[ wordcount ] != NULL; ++wordcount ) { + ; + } + } + + f = filtbuf; + flimit = filtbuf + buflen - 1; + + if ( prefix != NULL ) { + f = filter_add_strn( f, flimit, prefix, strlen( prefix )); + } + + for ( p = pattern; f != NULL && *p != '\0'; ++p ) { + if ( *p == '%' ) { + ++p; + if ( *p == 'v' || *p == 'e' ) { + escape_all = ( *p == 'e' ); + if ( ldap_utf8isdigit( p+1 )) { + ++p; + wordnum = *p - '1'; + if ( *(p+1) == '-' ) { + ++p; + if ( ldap_utf8isdigit( p+1 )) { + ++p; + endwordnum = *p - '1'; /* e.g., "%v2-4" */ + if ( endwordnum > wordcount - 1 ) { + endwordnum = wordcount - 1; + } + } else { + endwordnum = wordcount - 1; /* e.g., "%v2-" */ + } + } else { + endwordnum = wordnum; /* e.g., "%v2" */ + } + + if ( wordcount > 0 ) { + for ( i = wordnum; i <= endwordnum; ++i ) { + if ( i > wordnum ) { /* add blank btw words */ + f = filter_add_strn( f, flimit, " ", 1 ); + if ( f == NULL ) break; + } + f = filter_add_value( f, flimit, valwords[ i ], + escape_all ); + if ( f == NULL ) break; + } + } + } else if ( *(p+1) == '$' ) { + ++p; + if ( wordcount > 0 ) { + wordnum = wordcount - 1; + f = filter_add_value( f, flimit, + valwords[ wordnum ], escape_all ); + } + } else if ( value != NULL ) { + f = filter_add_value( f, flimit, value, escape_all ); + } + } else if ( *p == 'a' && attr != NULL ) { + f = filter_add_strn( f, flimit, attr, strlen( attr )); + } else { + *f++ = *p; + } + } else { + *f++ = *p; + } + if ( f > flimit ) { /* overflow */ + f = NULL; + } + } + + if ( suffix != NULL && f != NULL) { + f = filter_add_strn( f, flimit, suffix, strlen( suffix )); + } + + if ( f == NULL ) { + *flimit = '\0'; + return( LDAP_SIZELIMIT_EXCEEDED ); + } + *f = '\0'; + return( LDAP_SUCCESS ); +} + + +/* + * ldap_build_filter() is deprecated -- use ldap_create_filter() instead + */ +void +LDAP_CALL +ldap_build_filter( char *filtbuf, unsigned long buflen, char *pattern, + char *prefix, char *suffix, char *attr, char *value, char **valwords ) +{ + (void)ldap_create_filter( filtbuf, buflen, pattern, prefix, suffix, attr, + value, valwords ); +} + + +static int +break_into_words( char *str, char *delims, char ***wordsp ) +{ + char *word, **words; + int count; + char *lasts; + + if (( words = (char **)NSLDAPI_CALLOC( 1, sizeof( char * ))) == NULL ) { + return( -1 ); + } + count = 0; + words[ count ] = NULL; + + word = ldap_utf8strtok_r( str, delims, &lasts ); + while ( word != NULL ) { + if (( words = (char **)NSLDAPI_REALLOC( words, + ( count + 2 ) * sizeof( char * ))) == NULL ) { + return( -1 ); + } + + words[ count ] = word; + words[ ++count ] = NULL; + word = ldap_utf8strtok_r( NULL, delims, &lasts ); + } + + *wordsp = words; + return( count ); +} diff --git a/ldap/c-sdk/libraries/libldap/getoption.c b/ldap/c-sdk/libraries/libldap/getoption.c new file mode 100644 index 0000000000..73e21a2dc9 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getoption.c @@ -0,0 +1,475 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +#define LDAP_GET_BITOPT( ld, bit ) \ + ((ld)->ld_options & bit ) != 0 ? 1 : 0 + +static int nsldapi_get_api_info( LDAPAPIInfo *aip ); +static int nsldapi_get_feature_info( LDAPAPIFeatureInfo *fip ); + + +int +LDAP_CALL +ldap_get_option( LDAP *ld, int option, void *optdata ) +{ + int rc = 0; + + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + + /* + * optdata MUST be a valid pointer... + */ + if (NULL == optdata) + { + return(LDAP_PARAM_ERROR); + } + /* + * process global options (not associated with an LDAP session handle) + */ + if ( option == LDAP_OPT_MEMALLOC_FN_PTRS ) { + /* struct copy */ + *((struct ldap_memalloc_fns *)optdata) = nsldapi_memalloc_fns; + return( 0 ); + } + + if ( option == LDAP_OPT_API_INFO ) { + rc = nsldapi_get_api_info( (LDAPAPIInfo *)optdata ); + if ( rc != LDAP_SUCCESS ) { + if ( ld != NULL ) { + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + } + return( -1 ); + } + return( 0 ); + } + /* + * LDAP_OPT_DEBUG_LEVEL is global + */ + if (LDAP_OPT_DEBUG_LEVEL == option) + { +#ifdef LDAP_DEBUG + *((int *) optdata) = ldap_debug; +#endif /* LDAP_DEBUG */ + return ( 0 ); + } + + /* + * if ld is NULL, arrange to return options from our default settings + */ + if ( ld == NULL ) { + ld = &nsldapi_ld_defaults; + } + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); /* punt */ + } + + + if (ld != &nsldapi_ld_defaults) + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + switch( option ) { +#ifdef LDAP_DNS + case LDAP_OPT_DNS: + *((int *) optdata) = LDAP_GET_BITOPT( ld, LDAP_BITOPT_DNS ); + break; +#endif + + case LDAP_OPT_REFERRALS: + *((int *) optdata) = + LDAP_GET_BITOPT( ld, LDAP_BITOPT_REFERRALS ); + break; + + case LDAP_OPT_SSL: + *((int *) optdata) = LDAP_GET_BITOPT( ld, LDAP_BITOPT_SSL ); + break; + + case LDAP_OPT_RESTART: + *((int *) optdata) = LDAP_GET_BITOPT( ld, LDAP_BITOPT_RESTART ); + break; + + case LDAP_OPT_RECONNECT: + *((int *) optdata) = + LDAP_GET_BITOPT( ld, LDAP_BITOPT_RECONNECT ); + break; + + case LDAP_OPT_NOREBIND: + *((int *) optdata) = + LDAP_GET_BITOPT( ld, LDAP_BITOPT_NOREBIND ); + break; + +#ifdef LDAP_ASYNC_IO + case LDAP_OPT_ASYNC_CONNECT: + *((int *) optdata) = + LDAP_GET_BITOPT( ld, LDAP_BITOPT_ASYNC ); + break; +#endif /* LDAP_ASYNC_IO */ + + /* stuff in the sockbuf */ + case LDAP_X_OPT_SOCKBUF: + *((Sockbuf **) optdata) = ld->ld_sbp; + break; + + case LDAP_OPT_DESC: + if ( ber_sockbuf_get_option( ld->ld_sbp, + LBER_SOCKBUF_OPT_DESC, optdata ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + rc = -1; + } + break; + + /* fields in the LDAP structure */ + case LDAP_OPT_DEREF: + *((int *) optdata) = ld->ld_deref; + break; + case LDAP_OPT_SIZELIMIT: + *((int *) optdata) = ld->ld_sizelimit; + break; + case LDAP_OPT_TIMELIMIT: + *((int *) optdata) = ld->ld_timelimit; + break; + case LDAP_OPT_REFERRAL_HOP_LIMIT: + *((int *) optdata) = ld->ld_refhoplimit; + break; + case LDAP_OPT_PROTOCOL_VERSION: + *((int *) optdata) = ld->ld_version; + break; + case LDAP_OPT_SERVER_CONTROLS: + /* fall through */ + case LDAP_OPT_CLIENT_CONTROLS: + *((LDAPControl ***)optdata) = NULL; + /* nsldapi_dup_controls returns -1 and sets lderrno on error */ + rc = nsldapi_dup_controls( ld, (LDAPControl ***)optdata, + ( option == LDAP_OPT_SERVER_CONTROLS ) ? + ld->ld_servercontrols : ld->ld_clientcontrols ); + break; + + /* rebind proc */ + case LDAP_OPT_REBIND_FN: + *((LDAP_REBINDPROC_CALLBACK **) optdata) = ld->ld_rebind_fn; + break; + case LDAP_OPT_REBIND_ARG: + *((void **) optdata) = ld->ld_rebind_arg; + break; + + /* i/o function pointers */ + case LDAP_OPT_IO_FN_PTRS: + if ( ld->ld_io_fns_ptr == NULL ) { + memset( optdata, 0, sizeof( struct ldap_io_fns )); + } else { + /* struct copy */ + *((struct ldap_io_fns *)optdata) = *(ld->ld_io_fns_ptr); + } + break; + + /* extended i/o function pointers */ + case LDAP_X_OPT_EXTIO_FN_PTRS: + if ( ((struct ldap_x_ext_io_fns *) optdata)->lextiof_size == LDAP_X_EXTIO_FNS_SIZE_REV0) { + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_close = ld->ld_extclose_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_connect = ld->ld_extconnect_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_read = ld->ld_extread_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_write = ld->ld_extwrite_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_poll = ld->ld_extpoll_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_newhandle = ld->ld_extnewhandle_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_disposehandle = ld->ld_extdisposehandle_fn; + ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_session_arg = ld->ld_ext_session_arg; + } else if ( ((struct ldap_x_ext_io_fns *) optdata)->lextiof_size == + LDAP_X_EXTIO_FNS_SIZE ) { + /* struct copy */ + *((struct ldap_x_ext_io_fns *) optdata) = ld->ld_ext_io_fns; + } else { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + rc = -1; + } + break; + + /* get socketargp in extended i/o function */ + case LDAP_X_OPT_SOCKETARG: + if ( ber_sockbuf_get_option( ld->ld_sbp,LBER_SOCKBUF_OPT_SOCK_ARG, optdata) + != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + rc = -1; + } + break; + + /* thread function pointers */ + case LDAP_OPT_THREAD_FN_PTRS: + /* struct copy */ + *((struct ldap_thread_fns *) optdata) = ld->ld_thread; + break; + + /* extra thread function pointers */ + case LDAP_OPT_EXTRA_THREAD_FN_PTRS: + /* struct copy */ + *((struct ldap_extra_thread_fns *) optdata) = ld->ld_thread2; + break; + + /* DNS function pointers */ + case LDAP_OPT_DNS_FN_PTRS: + /* struct copy */ + *((struct ldap_dns_fns *) optdata) = ld->ld_dnsfn; + break; + + /* cache function pointers */ + case LDAP_OPT_CACHE_FN_PTRS: + /* struct copy */ + *((struct ldap_cache_fns *) optdata) = ld->ld_cache; + break; + case LDAP_OPT_CACHE_STRATEGY: + *((int *) optdata) = ld->ld_cache_strategy; + break; + case LDAP_OPT_CACHE_ENABLE: + *((int *) optdata) = ld->ld_cache_on; + break; + + case LDAP_OPT_ERROR_NUMBER: + *((int *) optdata) = LDAP_GET_LDERRNO( ld, NULL, NULL ); + break; + + case LDAP_OPT_ERROR_STRING: + (void)LDAP_GET_LDERRNO( ld, NULL, (char **)optdata ); + *((char **) optdata) = nsldapi_strdup( *((char **) optdata )); + break; + + case LDAP_OPT_MATCHED_DN: + (void)LDAP_GET_LDERRNO( ld, (char **)optdata, NULL ); + *((char **) optdata) = nsldapi_strdup( *((char **) optdata )); + break; + + case LDAP_OPT_PREFERRED_LANGUAGE: + if ( NULL != ld->ld_preferred_language ) { + *((char **) optdata) = + nsldapi_strdup(ld->ld_preferred_language); + } else { + *((char **) optdata) = NULL; + } + break; + + case LDAP_OPT_API_FEATURE_INFO: + rc = nsldapi_get_feature_info( (LDAPAPIFeatureInfo *)optdata ); + if ( rc != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + rc = -1; + } + break; + + case LDAP_OPT_HOST_NAME: + *((char **) optdata) = nsldapi_strdup( ld->ld_defhost ); + break; + + case LDAP_X_OPT_CONNECT_TIMEOUT: + *((int *) optdata) = ld->ld_connect_timeout; + break; + +#ifdef LDAP_SASLIO_HOOKS + /* SASL options */ + case LDAP_OPT_X_SASL_MECH: + *((char **) optdata) = nsldapi_strdup(ld->ld_def_sasl_mech); + break; + case LDAP_OPT_X_SASL_REALM: + *((char **) optdata) = nsldapi_strdup(ld->ld_def_sasl_realm); + break; + case LDAP_OPT_X_SASL_AUTHCID: + *((char **) optdata) = nsldapi_strdup(ld->ld_def_sasl_authcid); + break; + case LDAP_OPT_X_SASL_AUTHZID: + *((char **) optdata) = nsldapi_strdup(ld->ld_def_sasl_authzid); + break; + case LDAP_OPT_X_SASL_SSF: + { + int sc; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; + if( ld->ld_defconn == NULL ) { + return -1; + } + ctx = (sasl_conn_t *)(ld->ld_defconn->lconn_sasl_ctx); + if ( ctx == NULL ) { + return -1; + } + sc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf ); + if ( sc != SASL_OK ) { + return -1; + } + *((sasl_ssf_t *) optdata) = *ssf; + } + break; + case LDAP_OPT_X_SASL_SSF_MIN: + *((sasl_ssf_t *) optdata) = ld->ld_sasl_secprops.min_ssf; + break; + case LDAP_OPT_X_SASL_SSF_MAX: + *((sasl_ssf_t *) optdata) = ld->ld_sasl_secprops.max_ssf; + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + *((sasl_ssf_t *) optdata) = ld->ld_sasl_secprops.maxbufsize; + break; + case LDAP_OPT_X_SASL_SSF_EXTERNAL: + case LDAP_OPT_X_SASL_SECPROPS: + /* + * These options are write only. Making these options + * read/write would expose semi-private interfaces of libsasl + * for which there are no cross platform/standardized + * definitions. + */ + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + rc = -1; + break; +#endif + + default: + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + rc = -1; + } + if (ld != &nsldapi_ld_defaults) + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + return( rc ); +} + + +/* + * Table of extended API features we support. + * The first field is the version of the info. strcuture itself; we do not + * use the ones from this table so it is okay to leave as zero. + */ +static LDAPAPIFeatureInfo nsldapi_extensions[] = { + { 0, "SERVER_SIDE_SORT", LDAP_API_FEATURE_SERVER_SIDE_SORT }, + { 0, "VIRTUAL_LIST_VIEW", LDAP_API_FEATURE_VIRTUAL_LIST_VIEW }, + { 0, "PERSISTENT_SEARCH", LDAP_API_FEATURE_PERSISTENT_SEARCH }, + { 0, "PROXY_AUTHORIZATION", LDAP_API_FEATURE_PROXY_AUTHORIZATION }, + { 0, "X_LDERRNO", LDAP_API_FEATURE_X_LDERRNO }, + { 0, "X_MEMCACHE", LDAP_API_FEATURE_X_MEMCACHE }, + { 0, "X_IO_FUNCTIONS", LDAP_API_FEATURE_X_IO_FUNCTIONS }, + { 0, "X_EXTIO_FUNCTIONS", LDAP_API_FEATURE_X_EXTIO_FUNCTIONS }, + { 0, "X_DNS_FUNCTIONS", LDAP_API_FEATURE_X_DNS_FUNCTIONS }, + { 0, "X_MEMALLOC_FUNCTIONS", LDAP_API_FEATURE_X_MEMALLOC_FUNCTIONS }, + { 0, "X_THREAD_FUNCTIONS", LDAP_API_FEATURE_X_THREAD_FUNCTIONS }, + { 0, "X_EXTHREAD_FUNCTIONS", LDAP_API_FEATURE_X_EXTHREAD_FUNCTIONS }, + { 0, "X_GETLANGVALUES", LDAP_API_FEATURE_X_GETLANGVALUES }, + { 0, "X_CLIENT_SIDE_SORT", LDAP_API_FEATURE_X_CLIENT_SIDE_SORT }, + { 0, "X_URL_FUNCTIONS", LDAP_API_FEATURE_X_URL_FUNCTIONS }, + { 0, "X_FILTER_FUNCTIONS", LDAP_API_FEATURE_X_FILTER_FUNCTIONS }, +}; + +#define NSLDAPI_EXTENSIONS_COUNT \ + (sizeof(nsldapi_extensions)/sizeof(LDAPAPIFeatureInfo)) + +/* + * Retrieve information about this implementation of the LDAP API. + * Returns an LDAP error code. + */ +static int +nsldapi_get_api_info( LDAPAPIInfo *aip ) +{ + int i; + + if ( aip == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + aip->ldapai_api_version = LDAP_API_VERSION; + + if ( aip->ldapai_info_version != LDAP_API_INFO_VERSION ) { + aip->ldapai_info_version = LDAP_API_INFO_VERSION; + return( LDAP_PARAM_ERROR ); + } + + aip->ldapai_protocol_version = LDAP_VERSION_MAX; + aip->ldapai_vendor_version = LDAP_VENDOR_VERSION; + + if (( aip->ldapai_vendor_name = nsldapi_strdup( LDAP_VENDOR_NAME )) + == NULL ) { + return( LDAP_NO_MEMORY ); + } + + if ( NSLDAPI_EXTENSIONS_COUNT < 1 ) { + aip->ldapai_extensions = NULL; + } else { + if (( aip->ldapai_extensions = NSLDAPI_CALLOC( + NSLDAPI_EXTENSIONS_COUNT + 1, sizeof(char *))) == NULL ) { + NSLDAPI_FREE( aip->ldapai_vendor_name ); + aip->ldapai_vendor_name = NULL; + return( LDAP_NO_MEMORY ); + } + + for ( i = 0; i < NSLDAPI_EXTENSIONS_COUNT; ++i ) { + if (( aip->ldapai_extensions[i] = nsldapi_strdup( + nsldapi_extensions[i].ldapaif_name )) == NULL ) { + ldap_value_free( aip->ldapai_extensions ); + NSLDAPI_FREE( aip->ldapai_vendor_name ); + aip->ldapai_extensions = NULL; + aip->ldapai_vendor_name = NULL; + return( LDAP_NO_MEMORY ); + } + } + } + + return( LDAP_SUCCESS ); +} + + +/* + * Retrieves information about a specific extended feature of the LDAP API/ + * Returns an LDAP error code. + */ +static int +nsldapi_get_feature_info( LDAPAPIFeatureInfo *fip ) +{ + int i; + + if ( fip == NULL || fip->ldapaif_name == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + if ( fip->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION ) { + fip->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; + return( LDAP_PARAM_ERROR ); + } + + for ( i = 0; i < NSLDAPI_EXTENSIONS_COUNT; ++i ) { + if ( strcmp( fip->ldapaif_name, + nsldapi_extensions[i].ldapaif_name ) == 0 ) { + fip->ldapaif_version = + nsldapi_extensions[i].ldapaif_version; + break; + } + } + + return(( i < NSLDAPI_EXTENSIONS_COUNT ) ? LDAP_SUCCESS + : LDAP_PARAM_ERROR ); +} diff --git a/ldap/c-sdk/libraries/libldap/getvalues.c b/ldap/c-sdk/libraries/libldap/getvalues.c new file mode 100644 index 0000000000..cae40d2cdf --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/getvalues.c @@ -0,0 +1,480 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * getvalues.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + + +static void ** +internal_ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target, + int lencall ) +{ + struct berelement ber; + char *attr; + int rc; + void **vals; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULL ); /* punt */ + } + if ( target == NULL || + !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( NULL ); + } + + ber = *entry->lm_ber; + + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( NULL ); + } + + rc = strcasecmp( (char *)target, attr ); + NSLDAPI_FREE( attr ); + if ( rc != 0 ) { + while ( 1 ) { + if ( ber_scanf( &ber, "x}{a", &attr ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, + NULL, NULL ); + return( NULL ); + } + + rc = strcasecmp( (char *)target, attr ); + if ( rc == 0 ) { + NSLDAPI_FREE( attr ); + break; + } + NSLDAPI_FREE( attr ); + } + } + + /* + * if we get this far, we've found the attribute and are sitting + * just before the set of values. + */ + + if ( lencall ) { + rc = ber_scanf( &ber, "[V]", &vals ); + } else { + rc = ber_scanf( &ber, "[v]", &vals ); + } + + if ( rc == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + } else { + rc = LDAP_SUCCESS; + } + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + + return(( rc == LDAP_SUCCESS ) ? vals : NULL ); +} + + +/* For language-sensitive attribute matching, we are looking for a + language tag that looks like one of the following: + + cn + cn;lang-en + cn;lang-en-us + cn;lang-ja + cn;lang-ja-JP-kanji + + The base language specification consists of two letters following + "lang-". After that, there may be additional language-specific + narrowings preceded by a "-". In our processing we go from the + specific to the general, preferring a complete subtype match, but + accepting a partial one. For example: + + For a request for "cn;lang-en-us", we would return cn;lang-en-us + if present, otherwise cn;lang-en if present, otherwise cn. + + Besides the language subtype, there may be other subtypes: + + cn;lang-ja;binary (Unlikely!) + cn;lang-ja;phonetic + + If not in the target, they are ignored. If they are in the target, + they must be in the attribute to match. +*/ +#define LANG_SUBTYPE_INDEX_NONE -1 +#define LANG_SUBTYPE_INDEX_DUPLICATE -2 + +typedef struct { + int start; + int length; +} _SubStringIndex; + +static int +parse_subtypes( const char *target, int *baseLenp, char **langp, + _SubStringIndex **subs, int *nsubtypes ) +{ + int nSubtypes = 0; + int ind = 0; + char *nextToken; + _SubStringIndex *result = NULL; + int langIndex; + int targetLen; + int subtypeStart; + + langIndex = LANG_SUBTYPE_INDEX_NONE; + *subs = NULL; + *langp = NULL; + *baseLenp = 0; + *nsubtypes = 0; + targetLen = strlen( target ); + + /* Parse past base attribute */ + nextToken = strchr( target, ';' ); + if ( NULL != nextToken ) { + subtypeStart = nextToken - target + 1; + *baseLenp = subtypeStart - 1; + } + else { + subtypeStart = targetLen; + *baseLenp = subtypeStart; + } + ind = subtypeStart; + + /* How many subtypes? */ + nextToken = (char *)target + subtypeStart; + while ( nextToken && *nextToken ) { + char *thisToken = nextToken; + nextToken = strchr( thisToken, ';' ); + if ( NULL != nextToken ) + nextToken++; + if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) { + /* If there was a previous lang tag, this is illegal! */ + if ( langIndex != LANG_SUBTYPE_INDEX_NONE ) { + langIndex = LANG_SUBTYPE_INDEX_DUPLICATE; + return langIndex; + } + else { + langIndex = nSubtypes; + } + } else { + nSubtypes++; + } + } + /* No language subtype? */ + if ( langIndex < 0 ) + return langIndex; + + /* Allocate array of non-language subtypes */ + if ( nSubtypes > 0 ) { + result = (_SubStringIndex *)NSLDAPI_MALLOC( sizeof(*result) + * nSubtypes ); + memset( result, 0, sizeof(*result) * nSubtypes ); + } + ind = 0; + nSubtypes = 0; + ind = subtypeStart; + nextToken = (char *)target + subtypeStart; + while ( nextToken && *nextToken ) { + char *thisToken = nextToken; + int len; + nextToken = strchr( thisToken, ';' ); + if ( NULL != nextToken ) { + len = nextToken - thisToken; + nextToken++; + } + else { + nextToken = (char *)target + targetLen; + len = nextToken - thisToken; + } + if ( 0 == strncasecmp( thisToken, "lang-", 5 ) ) { + int i; + *langp = (char *)NSLDAPI_MALLOC( len + 1 ); + for( i = 0; i < len; i++ ) + (*langp)[i] = toupper( target[ind+i] ); + (*langp)[len] = 0; + } + else { + result[nSubtypes].start = thisToken - target; + result[nSubtypes].length = len; + nSubtypes++; + } + } + *subs = result; + *nsubtypes = nSubtypes; + return langIndex; +} + + +static int +check_lang_match( const char *target, const char *baseTarget, + _SubStringIndex *targetTypes, + int ntargetTypes, char *targetLang, char *attr ) +{ + int langIndex; + _SubStringIndex *subtypes; + int baseLen; + char *lang; + int nsubtypes; + int mismatch = 0; + int match = -1; + int i; + + /* Get all subtypes in the attribute name */ + langIndex = parse_subtypes( attr, &baseLen, &lang, &subtypes, &nsubtypes ); + + /* Check if there any required non-language subtypes which are + not in this attribute */ + for( i = 0; i < ntargetTypes; i++ ) { + char *t = (char *)target+targetTypes[i].start; + int tlen = targetTypes[i].length; + int j; + for( j = 0; j < nsubtypes; j++ ) { + char *a = attr + subtypes[j].start; + int alen = subtypes[j].length; + if ( (tlen == alen) && !strncasecmp( t, a, tlen ) ) + break; + } + if ( j >= nsubtypes ) { + mismatch = 1; + break; + } + } + if ( mismatch ) { + if ( NULL != subtypes ) + NSLDAPI_FREE( subtypes ); + if ( NULL != lang ) + NSLDAPI_FREE( lang ); + return -1; + } + + /* If there was no language subtype... */ + if ( langIndex < 0 ) { + if ( NULL != subtypes ) + NSLDAPI_FREE( subtypes ); + if ( NULL != lang ) + NSLDAPI_FREE( lang ); + if ( LANG_SUBTYPE_INDEX_NONE == langIndex ) + return 0; + else + return -1; + } + + /* Okay, now check the language subtag */ + i = 0; + while( targetLang[i] && lang[i] && + (toupper(targetLang[i]) == toupper(lang[i])) ) + i++; + + /* The total length can't be longer than the requested subtype */ + if ( !lang[i] || (lang[i] == ';') ) { + /* If the found subtype is shorter than the requested one, the next + character in the requested one should be "-" */ + if ( !targetLang[i] || (targetLang[i] == '-') ) + match = i; + } + return match; +} + +static int check_base_match( const char *target, char *attr ) +{ + int i = 0; + int rc; + while( target[i] && attr[i] && (toupper(target[i]) == toupper(attr[i])) ) + i++; + rc = ( !target[i] && (!attr[i] || (';' == attr[i])) ); + return rc; +} + +static void ** +internal_ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, + const char *target, char **type, int lencall ) +{ + struct berelement ber; + char *attr = NULL; + int rc; + void **vals = NULL; + int langIndex; + _SubStringIndex *subtypes; + int nsubtypes; + char *baseTarget = NULL; + int bestMatch = 0; + char *lang = NULL; + int len; + int firstAttr = 1; + char *bestType = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULL ); + } + if ( (target == NULL) || + !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( NULL ); + } + + /* A language check was requested, so see if there really is a + language subtype in the attribute spec */ + langIndex = parse_subtypes( target, &len, &lang, + &subtypes, &nsubtypes ); + if ( langIndex < 0 ) { + if ( NULL != subtypes ) { + NSLDAPI_FREE( subtypes ); + subtypes = NULL; + } + vals = internal_ldap_get_values( ld, entry, target, lencall ); + if ( NULL != type ) + *type = nsldapi_strdup( target ); + return vals; + } else { + /* Get just the base attribute name */ + baseTarget = (char *)NSLDAPI_MALLOC( len + 1 ); + memcpy( baseTarget, target, len ); + baseTarget[len] = 0; + } + + ber = *entry->lm_ber; + + /* Process all attributes in the entry */ + while ( 1 ) { + int foundMatch = 0; + if ( NULL != attr ) + NSLDAPI_FREE( attr ); + if ( firstAttr ) { + firstAttr = 0; + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a", &attr ) == LBER_ERROR ) { + break; + } + } else { + if ( ber_scanf( &ber, "{a", &attr ) == LBER_ERROR ) { + break; + } + } + + if ( check_base_match( (const char *)baseTarget, attr ) ) { + int thisMatch = check_lang_match( target, baseTarget, + subtypes, nsubtypes, lang, attr ); + if ( thisMatch > bestMatch ) { + if ( vals ) + NSLDAPI_FREE( vals ); + foundMatch = 1; + bestMatch = thisMatch; + if ( NULL != bestType ) + NSLDAPI_FREE( bestType ); + bestType = attr; + attr = NULL; + } + } + if ( foundMatch ) { + if ( lencall ) { + rc = ber_scanf( &ber, "[V]}", &vals ); + } else { + rc = ber_scanf( &ber, "[v]}", &vals ); + } + } else { + ber_scanf( &ber, "x}" ); + } + } + + NSLDAPI_FREE( lang ); + NSLDAPI_FREE( baseTarget ); + NSLDAPI_FREE( subtypes ); + + if ( NULL != type ) + *type = bestType; + else if ( NULL != bestType ) + NSLDAPI_FREE( bestType ); + + if ( NULL == vals ) { + rc = LDAP_DECODING_ERROR; + } else { + rc = LDAP_SUCCESS; + } + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + + return( vals ); +} + + +char ** +LDAP_CALL +ldap_get_values( LDAP *ld, LDAPMessage *entry, const char *target ) +{ + return( (char **) internal_ldap_get_values( ld, entry, target, 0 ) ); +} + +struct berval ** +LDAP_CALL +ldap_get_values_len( LDAP *ld, LDAPMessage *entry, const char *target ) +{ + return( (struct berval **) internal_ldap_get_values( ld, entry, target, + 1 ) ); +} + +char ** +LDAP_CALL +ldap_get_lang_values( LDAP *ld, LDAPMessage *entry, const char *target, + char **type ) +{ + return( (char **) internal_ldap_get_lang_values( ld, entry, + target, type, 0 ) ); +} + +struct berval ** +LDAP_CALL +ldap_get_lang_values_len( LDAP *ld, LDAPMessage *entry, const char *target, + char **type ) +{ + return( (struct berval **) internal_ldap_get_lang_values( ld, entry, + target, type, 1 ) ); +} + diff --git a/ldap/c-sdk/libraries/libldap/ldap-int.h b/ldap/c-sdk/libraries/libldap/ldap-int.h new file mode 100644 index 0000000000..f64d1b3d3a --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/ldap-int.h @@ -0,0 +1,888 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#ifndef _LDAPINT_H +#define _LDAPINT_H + +#include +#include +#include +#include +#include +#include +#ifdef hpux +#include +#endif /* hpux */ + +#ifdef _WINDOWS +# define FD_SETSIZE 256 /* number of connections we support */ +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(macintosh) +#include "ldap-macos.h" +#else /* _WINDOWS */ +# include +# include +# include +# include +#if !defined(XP_OS2) && !defined(XP_BEOS) +# include +#endif +# include +#if !defined(hpux) && !defined(SUNOS4) && !defined(XP_BEOS) +# include +#endif /* !defined(hpux) and others */ +#endif /* _WINDOWS */ + +#if defined(IRIX) +#include +#endif /* IRIX */ + +#ifdef XP_BEOS +#define NSLDAPI_AVOID_OS_SOCKETS +#endif + +#define NSLBERI_LBER_INT_FRIEND +#ifdef macintosh +#include "lber-int.h" +#else /* macintosh */ +#include "../liblber/lber-int.h" +#endif /* macintosh */ + +#include "ldap.h" +#include "ldaprot.h" +#include "ldaplog.h" +#include "portable.h" + +#ifdef LDAP_ASYNC_IO +#ifdef NEED_FILIO +#include /* to get FIONBIO for ioctl() call */ +#else /* NEED_FILIO */ +#if !defined( _WINDOWS) && !defined (macintosh) +#include /* to get FIONBIO for ioctl() call */ +#endif /* _WINDOWS && macintosh */ +#endif /* NEED_FILIO */ +#endif /* LDAP_ASYNC_IO */ + +#ifdef USE_SYSCONF +# include +#endif /* USE_SYSCONF */ + +#ifdef LDAP_SASLIO_HOOKS +#include +#define SASL_MAX_BUFF_SIZE 65536 +#define SASL_MIN_BUFF_SIZE 4096 +#endif + +#if !defined(_WINDOWS) && !defined(macintosh) && !defined(BSDI) && \ + !defined(XP_OS2) && !defined(XP_BEOS) && !defined(NTO) && \ + !defined(DARWIN) +#define NSLDAPI_HAVE_POLL 1 +#endif + +/* SSL version, or 0 if not built with SSL */ +#if defined(NET_SSL) +# define SSL_VERSION 3 +#else +# define SSL_VERSION 0 +#endif + + +#define LDAP_URL_URLCOLON "URL:" +#define LDAP_URL_URLCOLON_LEN 4 + +#define LDAP_LDAP_REF_STR LDAP_URL_PREFIX +#define LDAP_LDAP_REF_STR_LEN LDAP_URL_PREFIX_LEN +#define LDAP_LDAPS_REF_STR LDAPS_URL_PREFIX +#define LDAP_LDAPS_REF_STR_LEN LDAPS_URL_PREFIX_LEN + +/* default limit on nesting of referrals */ +#define LDAP_DEFAULT_REFHOPLIMIT 5 +#ifdef LDAP_DNS +#define LDAP_DX_REF_STR "dx://" +#define LDAP_DX_REF_STR_LEN 5 +#endif /* LDAP_DNS */ + +typedef enum { + LDAP_CACHE_LOCK, + LDAP_MEMCACHE_LOCK, + LDAP_MSGID_LOCK, + LDAP_REQ_LOCK, + LDAP_RESP_LOCK, + LDAP_ABANDON_LOCK, + LDAP_CTRL_LOCK, + LDAP_OPTION_LOCK, + LDAP_ERR_LOCK, + LDAP_CONN_LOCK, + LDAP_IOSTATUS_LOCK, /* serializes access to ld->ld_iostatus */ + LDAP_RESULT_LOCK, + LDAP_PEND_LOCK, + LDAP_THREADID_LOCK, +#ifdef LDAP_SASLIO_HOOKS + LDAP_SASL_LOCK, +#endif + LDAP_MAX_LOCK +} LDAPLock; + +/* + * This structure represents both ldap messages and ldap responses. + * These are really the same, except in the case of search responses, + * where a response has multiple messages. + */ + +struct ldapmsg { + int lm_msgid; /* the message id */ + ber_tag_t lm_msgtype; /* the message type */ + BerElement *lm_ber; /* the ber encoded message contents */ + struct ldapmsg *lm_chain; /* for search - next msg in the resp */ + struct ldapmsg *lm_next; /* next response */ + int lm_fromcache; /* memcache: origin of message */ +}; + +/* + * structure for tracking LDAP server host, ports, DNs, etc. + */ +typedef struct ldap_server { + char *lsrv_host; + char *lsrv_dn; /* if NULL, use default */ + int lsrv_port; + unsigned long lsrv_options; /* boolean options */ +#define LDAP_SRV_OPT_SECURE 0x01 + struct ldap_server *lsrv_next; +} LDAPServer; + +/* + * structure for representing an LDAP server connection + */ +typedef struct ldap_conn { + Sockbuf *lconn_sb; + BerElement *lconn_ber; /* non-NULL if in midst of msg. */ + int lconn_version; /* LDAP protocol version */ + int lconn_refcnt; + unsigned long lconn_lastused; /* time */ + int lconn_status; +#define LDAP_CONNST_CONNECTING 2 +#define LDAP_CONNST_CONNECTED 3 +#define LDAP_CONNST_DEAD 4 + LDAPServer *lconn_server; + char *lconn_binddn; /* DN of last successful bind */ + int lconn_bound; /* has a bind been done? */ + int lconn_pending_requests; /* count of unsent req*/ + char *lconn_krbinstance; +#ifdef LDAP_SASLIO_HOOKS + sasl_conn_t *lconn_sasl_ctx; /* the sasl connection context */ +#endif /* LDAP_SASLIO_HOOKS */ + struct ldap_conn *lconn_next; +} LDAPConn; + + +/* + * structure used to track outstanding requests + */ +typedef struct ldapreq { + int lr_msgid; /* the message id */ + int lr_status; /* status of request */ +#define LDAP_REQST_INPROGRESS 1 +#define LDAP_REQST_CHASINGREFS 2 +#define LDAP_REQST_WRITING 4 +#define LDAP_REQST_CONNDEAD 5 /* associated conn. has failed */ + int lr_outrefcnt; /* count of outstanding referrals */ + int lr_origid; /* original request's message id */ + int lr_parentcnt; /* count of parent requests */ + ber_tag_t lr_res_msgtype; /* result message type */ + int lr_expect_resp; /* if non-zero, expect a response */ + int lr_res_errno; /* result LDAP errno */ + char *lr_res_error; /* result error string */ + char *lr_res_matched;/* result matched DN string */ + BerElement *lr_ber; /* ber encoded request contents */ + LDAPConn *lr_conn; /* connection used to send request */ + char *lr_binddn; /* request is a bind for this DN */ + struct ldapreq *lr_parent; /* request that spawned this referral */ + struct ldapreq *lr_child; /* list of requests we spawned */ + struct ldapreq *lr_sibling; /* next referral spawned */ + struct ldapreq *lr_prev; /* ld->ld_requests previous request */ + struct ldapreq *lr_next; /* ld->ld_requests next request */ + LDAPControl **lr_res_ctrls; /* result controls */ +} LDAPRequest; + +typedef struct ldappend { + void *lp_sema; /* semaphore to post */ + int lp_msgid; /* message id */ + LDAPMessage *lp_result; /* result storage */ + struct ldappend *lp_prev; /* previous pending */ + struct ldappend *lp_next; /* next pending */ +} LDAPPend; + +/* + * forward declaration for I/O status structure (defined in os-ip.c) + */ +typedef struct nsldapi_iostatus_info NSLDAPIIOStatus; + +/* + * old extended IO structure (before writev callback was added) + */ +struct ldap_x_ext_io_fns_rev0 { + int lextiof_size; + LDAP_X_EXTIOF_CONNECT_CALLBACK *lextiof_connect; + LDAP_X_EXTIOF_CLOSE_CALLBACK *lextiof_close; + LDAP_X_EXTIOF_READ_CALLBACK *lextiof_read; + LDAP_X_EXTIOF_WRITE_CALLBACK *lextiof_write; + LDAP_X_EXTIOF_POLL_CALLBACK *lextiof_poll; + LDAP_X_EXTIOF_NEWHANDLE_CALLBACK *lextiof_newhandle; + LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *lextiof_disposehandle; + void *lextiof_session_arg; +}; +#define LDAP_X_EXTIO_FNS_SIZE_REV0 sizeof(struct ldap_x_ext_io_fns_rev0) + + +/* + * Structure representing an ldap connection. + * + * This is an opaque struct; the fields are not visible to + * applications that use the LDAP API. + */ +struct ldap { + struct sockbuf *ld_sbp; /* pointer to socket desc. & buffer */ + char *ld_host; + int ld_version; /* LDAP protocol version */ + char ld_lberoptions; + int ld_deref; + + int ld_timelimit; + int ld_sizelimit; + + struct ldap_filt_desc *ld_filtd; /* from getfilter for ufn searches */ + char *ld_ufnprefix; /* for incomplete ufn's */ + + int ld_errno; + char *ld_error; + char *ld_matched; + int ld_msgid; + + /* Note: the ld_requests list is ordered old to new */ + LDAPRequest *ld_requests; /* list of outstanding requests */ + LDAPMessage *ld_responses; /* list of outstanding responses */ + int *ld_abandoned; /* array of abandoned requests */ + char *ld_cldapdn; /* DN used in connectionless search */ + + int ld_cldaptries; /* connectionless search retry count */ + int ld_cldaptimeout;/* time between retries */ + int ld_refhoplimit; /* limit on referral nesting */ + unsigned long ld_options; /* boolean options */ + +#define LDAP_BITOPT_REFERRALS 0x80000000 +#define LDAP_BITOPT_SSL 0x40000000 +#define LDAP_BITOPT_DNS 0x20000000 +#define LDAP_BITOPT_RESTART 0x10000000 +#define LDAP_BITOPT_RECONNECT 0x08000000 +#define LDAP_BITOPT_ASYNC 0x04000000 +#define LDAP_BITOPT_NOREBIND 0x02000000 + + char *ld_defhost; /* full name of default server */ + int ld_defport; /* port of default server */ + BERTranslateProc ld_lber_encode_translate_proc; + BERTranslateProc ld_lber_decode_translate_proc; + LDAPConn *ld_defconn; /* default connection */ + LDAPConn *ld_conns; /* list of all server connections */ + NSLDAPIIOStatus *ld_iostatus; /* status info. about network sockets */ + LDAP_REBINDPROC_CALLBACK *ld_rebind_fn; + void *ld_rebind_arg; + + /* function pointers, etc. for extended I/O */ + struct ldap_x_ext_io_fns ld_ext_io_fns; +#define ld_extio_size ld_ext_io_fns.lextiof_size +#define ld_extclose_fn ld_ext_io_fns.lextiof_close +#define ld_extconnect_fn ld_ext_io_fns.lextiof_connect +#define ld_extread_fn ld_ext_io_fns.lextiof_read +#define ld_extwrite_fn ld_ext_io_fns.lextiof_write +#define ld_extwritev_fn ld_ext_io_fns.lextiof_writev +#define ld_extpoll_fn ld_ext_io_fns.lextiof_poll +#define ld_extnewhandle_fn ld_ext_io_fns.lextiof_newhandle +#define ld_extdisposehandle_fn ld_ext_io_fns.lextiof_disposehandle +#define ld_ext_session_arg ld_ext_io_fns.lextiof_session_arg + + /* allocated pointer for older I/O functions */ + struct ldap_io_fns *ld_io_fns_ptr; +#define NSLDAPI_USING_CLASSIC_IO_FUNCTIONS( ld ) ((ld)->ld_io_fns_ptr != NULL) + + /* function pointers, etc. for DNS */ + struct ldap_dns_fns ld_dnsfn; +#define ld_dns_extradata ld_dnsfn.lddnsfn_extradata +#define ld_dns_bufsize ld_dnsfn.lddnsfn_bufsize +#define ld_dns_gethostbyname_fn ld_dnsfn.lddnsfn_gethostbyname +#define ld_dns_gethostbyaddr_fn ld_dnsfn.lddnsfn_gethostbyaddr +#define ld_dns_getpeername_fn ld_dnsfn.lddnsfn_getpeername + + /* function pointers, etc. for threading */ + struct ldap_thread_fns ld_thread; +#define ld_mutex_alloc_fn ld_thread.ltf_mutex_alloc +#define ld_mutex_free_fn ld_thread.ltf_mutex_free +#define ld_mutex_lock_fn ld_thread.ltf_mutex_lock +#define ld_mutex_unlock_fn ld_thread.ltf_mutex_unlock +#define ld_get_errno_fn ld_thread.ltf_get_errno +#define ld_set_errno_fn ld_thread.ltf_set_errno +#define ld_get_lderrno_fn ld_thread.ltf_get_lderrno +#define ld_set_lderrno_fn ld_thread.ltf_set_lderrno +#define ld_lderrno_arg ld_thread.ltf_lderrno_arg + void **ld_mutex; + + /* function pointers, etc. for caching */ + int ld_cache_on; + int ld_cache_strategy; + struct ldap_cache_fns ld_cache; +#define ld_cache_config ld_cache.lcf_config +#define ld_cache_bind ld_cache.lcf_bind +#define ld_cache_unbind ld_cache.lcf_unbind +#define ld_cache_search ld_cache.lcf_search +#define ld_cache_compare ld_cache.lcf_compare +#define ld_cache_add ld_cache.lcf_add +#define ld_cache_delete ld_cache.lcf_delete +#if 0 +#define ld_cache_rename ld_cache.lcf_rename +#endif +#define ld_cache_modify ld_cache.lcf_modify +#define ld_cache_modrdn ld_cache.lcf_modrdn +#define ld_cache_abandon ld_cache.lcf_abandon +#define ld_cache_result ld_cache.lcf_result +#define ld_cache_flush ld_cache.lcf_flush +#define ld_cache_arg ld_cache.lcf_arg + + /* ldapv3 controls */ + LDAPControl **ld_servercontrols; + LDAPControl **ld_clientcontrols; + + /* Preferred language */ + char *ld_preferred_language; + + /* MemCache */ + LDAPMemCache *ld_memcache; + + /* Pending results */ + LDAPPend *ld_pend; /* list of pending results */ + + /* extra thread function pointers */ + struct ldap_extra_thread_fns ld_thread2; + + /* + * With the 4.0 and later versions of the LDAP SDK, the extra thread + * functions except for the ld_threadid_fn have been disabled. + * Look at the release notes for the full explanation. + */ +#define ld_mutex_trylock_fn ld_thread2.ltf_mutex_trylock +#define ld_sema_alloc_fn ld_thread2.ltf_sema_alloc +#define ld_sema_free_fn ld_thread2.ltf_sema_free +#define ld_sema_wait_fn ld_thread2.ltf_sema_wait +#define ld_sema_post_fn ld_thread2.ltf_sema_post +#define ld_threadid_fn ld_thread2.ltf_threadid_fn + + /* extra data for mutex handling in referrals */ + void *ld_mutex_threadid[LDAP_MAX_LOCK]; + unsigned long ld_mutex_refcnt[LDAP_MAX_LOCK]; + + /* connect timeout value (milliseconds) */ + int ld_connect_timeout; + +#ifdef LDAP_SASLIO_HOOKS + /* SASL default option settings */ + char *ld_def_sasl_mech; + char *ld_def_sasl_realm; + char *ld_def_sasl_authcid; + char *ld_def_sasl_authzid; + /* SASL Security properties */ + struct sasl_security_properties ld_sasl_secprops; +#endif +}; + +/* allocate/free mutex */ +#define LDAP_MUTEX_ALLOC( ld ) \ + (((ld)->ld_mutex_alloc_fn != NULL) ? (ld)->ld_mutex_alloc_fn() : NULL) + +/* allocate/free mutex */ +#define LDAP_MUTEX_FREE( ld, m ) \ + if ( (ld)->ld_mutex_free_fn != NULL && m != NULL ) { \ + (ld)->ld_mutex_free_fn( m ); \ + } + +/* enter/exit critical sections */ +/* + * The locks assume that the locks are thread safe. XXXmcs: which means??? + * + * Note that we test for both ld_mutex_lock_fn != NULL AND ld_mutex != NULL. + * This is necessary because there is a window in ldap_init() between the + * time we set the ld_mutex_lock_fn pointer and the time we allocate the + * mutexes in which external code COULD be called which COULD make a call to + * something like ldap_get_option(), which uses LDAP_MUTEX_LOCK(). The + * libprldap code does this in its newhandle callback (prldap_newhandle). + */ + +#define LDAP_MUTEX_LOCK(ld, lock) \ + if ((ld)->ld_mutex_lock_fn != NULL && ld->ld_mutex != NULL) { \ + if ((ld)->ld_threadid_fn != NULL) { \ + if ((ld)->ld_mutex_threadid[lock] == (ld)->ld_threadid_fn()) { \ + (ld)->ld_mutex_refcnt[lock]++; \ + } else { \ + (ld)->ld_mutex_lock_fn(ld->ld_mutex[lock]); \ + (ld)->ld_mutex_threadid[lock] = ld->ld_threadid_fn(); \ + (ld)->ld_mutex_refcnt[lock] = 1; \ + } \ + } else { \ + (ld)->ld_mutex_lock_fn(ld->ld_mutex[lock]); \ + } \ + } + +#define LDAP_MUTEX_UNLOCK(ld, lock) \ + if ((ld)->ld_mutex_lock_fn != NULL && ld->ld_mutex != NULL) { \ + if ((ld)->ld_threadid_fn != NULL) { \ + if ((ld)->ld_mutex_threadid[lock] == (ld)->ld_threadid_fn()) { \ + (ld)->ld_mutex_refcnt[lock]--; \ + if ((ld)->ld_mutex_refcnt[lock] <= 0) { \ + (ld)->ld_mutex_threadid[lock] = (void *) -1; \ + (ld)->ld_mutex_refcnt[lock] = 0; \ + (ld)->ld_mutex_unlock_fn(ld->ld_mutex[lock]); \ + } \ + } \ + } else { \ + ld->ld_mutex_unlock_fn(ld->ld_mutex[lock]); \ + } \ + } + +/* Backward compatibility locks */ +#define LDAP_MUTEX_BC_LOCK( ld, i ) \ + /* the ld_mutex_trylock_fn is always set to NULL */ \ + /* in setoption.c as the extra thread functions were */ \ + /* turned off in the 4.0 SDK. This check will */ \ + /* always be true */ \ + if( (ld)->ld_mutex_trylock_fn == NULL ) { \ + LDAP_MUTEX_LOCK( ld, i ) ; \ + } +#define LDAP_MUTEX_BC_UNLOCK( ld, i ) \ + /* the ld_mutex_trylock_fn is always set to NULL */ \ + /* in setoption.c as the extra thread functions were */ \ + /* turned off in the 4.0 SDK. This check will */ \ + /* always be true */ \ + if( (ld)->ld_mutex_trylock_fn == NULL ) { \ + LDAP_MUTEX_UNLOCK( ld, i ) ; \ + } + +/* allocate/free semaphore */ +#define LDAP_SEMA_ALLOC( ld ) \ + (((ld)->ld_sema_alloc_fn != NULL) ? (ld)->ld_sema_alloc_fn() : NULL) +#define LDAP_SEMA_FREE( ld, m ) \ + if ( (ld)->ld_sema_free_fn != NULL && m != NULL ) { \ + (ld)->ld_sema_free_fn( m ); \ + } + +/* wait/post binary semaphore */ +#define LDAP_SEMA_WAIT( ld, lp ) \ + if ( (ld)->ld_sema_wait_fn != NULL ) { \ + (ld)->ld_sema_wait_fn( lp->lp_sema ); \ + } +#define LDAP_SEMA_POST( ld, lp ) \ + if ( (ld)->ld_sema_post_fn != NULL ) { \ + (ld)->ld_sema_post_fn( lp->lp_sema ); \ + } +#define POST( ld, y, z ) \ + /* the ld_mutex_trylock_fn is always set to NULL */ \ + /* in setoption.c as the extra thread functions were */ \ + /* turned off in the 4.0 SDK. This check will */ \ + /* always be false */ \ + if( (ld)->ld_mutex_trylock_fn != NULL ) { \ + nsldapi_post_result( ld, y, z ); \ + } + +/* get/set errno */ +#ifndef macintosh +#define LDAP_SET_ERRNO( ld, e ) \ + if ( (ld)->ld_set_errno_fn != NULL ) { \ + (ld)->ld_set_errno_fn( e ); \ + } else { \ + errno = e; \ + } +#define LDAP_GET_ERRNO( ld ) \ + (((ld)->ld_get_errno_fn != NULL) ? \ + (ld)->ld_get_errno_fn() : errno) +#else /* macintosh */ +#define LDAP_SET_ERRNO( ld, e ) \ + if ( (ld)->ld_set_errno_fn != NULL ) { \ + (ld)->ld_set_errno_fn( e ); \ + } +#define LDAP_GET_ERRNO( ld ) \ + (((ld)->ld_get_errno_fn != NULL) ? \ + (ld)->ld_get_errno_fn() : 0) +#endif + + +/* get/set ldap-specific errno */ +#define LDAP_SET_LDERRNO( ld, e, m, s ) ldap_set_lderrno( ld, e, m, s ) +#define LDAP_GET_LDERRNO( ld, m, s ) ldap_get_lderrno( ld, m, s ) + +/* + * your standard "mimimum of two values" macro + */ +#define NSLDAPI_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* + * handy macro to check whether LDAP struct is set up for CLDAP or not + */ +#define LDAP_IS_CLDAP( ld ) ( ld->ld_sbp->sb_naddr > 0 ) + +/* + * Some Unix error defs. Under CW 7, we can't define OTUNIXERRORS because + * it generates many conflicts with errno.h. Define what we need here. + * These need to be in sync with OpenTransport.h + */ + +#if defined(macintosh) +#define EWOULDBLOCK 35 +#define EHOSTUNREACH 65 +#endif + +/* + * handy macro to check errno "e" for an "in progress" sort of error + */ +#if defined(macintosh) || defined(_WINDOWS) +#define NSLDAPI_ERRNO_IO_INPROGRESS( e ) ((e) == EWOULDBLOCK || (e) == EAGAIN) +#else +#ifdef EAGAIN +#define NSLDAPI_ERRNO_IO_INPROGRESS( e ) ((e) == EWOULDBLOCK || (e) == EINPROGRESS || (e) == EAGAIN) +#else /* EAGAIN */ +#define NSLDAPI_ERRNO_IO_INPROGRESS( e ) ((e) == EWOULDBLOCK || (e) == EINPROGRESS) +#endif /* EAGAIN */ +#endif /* macintosh || _WINDOWS*/ + +/* + * macro to return the LDAP protocol version we are using + */ +#define NSLDAPI_LDAP_VERSION( ld ) ( (ld)->ld_defconn == NULL ? \ + (ld)->ld_version : \ + (ld)->ld_defconn->lconn_version ) + +/* + * Structures used for handling client filter lists. + */ +#define LDAP_FILT_MAXSIZ 1024 + +struct ldap_filt_list { + char *lfl_tag; + char *lfl_pattern; + char *lfl_delims; + struct ldap_filt_info *lfl_ilist; + struct ldap_filt_list *lfl_next; +}; + +struct ldap_filt_desc { + LDAPFiltList *lfd_filtlist; + LDAPFiltInfo *lfd_curfip; + LDAPFiltInfo lfd_retfi; + char lfd_filter[ LDAP_FILT_MAXSIZ ]; + char *lfd_curval; + char *lfd_curvalcopy; + char **lfd_curvalwords; + char *lfd_filtprefix; + char *lfd_filtsuffix; +}; + +/* + * "internal" globals used to track defaults and memory allocation callbacks: + * (the actual definitions are in open.c) + */ +extern struct ldap nsldapi_ld_defaults; +extern struct ldap_memalloc_fns nsldapi_memalloc_fns; +extern int nsldapi_initialized; + + +/* + * Memory allocation done in liblber should all go through one of the + * following macros. This is so we can plug-in alternative memory + * allocators, etc. as the need arises. + */ +#define NSLDAPI_MALLOC( size ) ldap_x_malloc( size ) +#define NSLDAPI_CALLOC( nelem, elsize ) ldap_x_calloc( nelem, elsize ) +#define NSLDAPI_REALLOC( ptr, size ) ldap_x_realloc( ptr, size ) +#define NSLDAPI_FREE( ptr ) ldap_x_free( ptr ) + + +/* + * macros used to check validity of data structures and parameters + */ +#define NSLDAPI_VALID_LDAP_POINTER( ld ) \ + ( (ld) != NULL ) + +#define NSLDAPI_VALID_LDAPMESSAGE_POINTER( lm ) \ + ( (lm) != NULL ) + +#define NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( lm ) \ + ( (lm) != NULL && (lm)->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) + +#define NSLDAPI_VALID_LDAPMESSAGE_REFERENCE_POINTER( lm ) \ + ( (lm) != NULL && (lm)->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) + +#define NSLDAPI_VALID_LDAPMESSAGE_BINDRESULT_POINTER( lm ) \ + ( (lm) != NULL && (lm)->lm_msgtype == LDAP_RES_BIND ) + +#define NSLDAPI_VALID_LDAPMESSAGE_EXRESULT_POINTER( lm ) \ + ( (lm) != NULL && (lm)->lm_msgtype == LDAP_RES_EXTENDED ) + +#define NSLDAPI_VALID_LDAPMOD_ARRAY( mods ) \ + ( (mods) != NULL ) + +#define NSLDAPI_VALID_NONEMPTY_LDAPMOD_ARRAY( mods ) \ + ( (mods) != NULL && (mods)[0] != NULL ) + +#define NSLDAPI_IS_SEARCH_ENTRY( code ) \ + ((code) == LDAP_RES_SEARCH_ENTRY) + +#define NSLDAPI_IS_SEARCH_RESULT( code ) \ + ((code) == LDAP_RES_SEARCH_RESULT) + +#define NSLDAPI_SEARCH_RELATED_RESULT( code ) \ + (NSLDAPI_IS_SEARCH_RESULT( code ) || NSLDAPI_IS_SEARCH_ENTRY( code )) + +/* + * in bind.c + */ +char *nsldapi_get_binddn( LDAP *ld ); + +/* + * in cache.c + */ +void nsldapi_add_result_to_cache( LDAP *ld, LDAPMessage *result ); + +/* + * in dsparse.c + */ +int nsldapi_next_line_tokens( char **bufp, long *blenp, char ***toksp ); +void nsldapi_free_strarray( char **sap ); + +/* + * in error.c + */ +int nsldapi_parse_result( LDAP *ld, int msgtype, BerElement *rber, + int *errcodep, char **matchednp, char **errmsgp, char ***referralsp, + LDAPControl ***serverctrlsp ); + +/* + * in open.c + */ +void nsldapi_initialize_defaults( void ); +void nsldapi_mutex_alloc_all( LDAP *ld ); +void nsldapi_mutex_free_all( LDAP *ld ); +int nsldapi_open_ldap_defconn( LDAP *ld ); +char *nsldapi_strdup( const char *s ); /* if s is NULL, returns NULL */ + +/* + * in os-ip.c + */ +int nsldapi_connect_to_host( LDAP *ld, Sockbuf *sb, const char *host, + int port, int secure, char **krbinstancep ); +void nsldapi_close_connection( LDAP *ld, Sockbuf *sb ); + +int nsldapi_iostatus_poll( LDAP *ld, struct timeval *timeout ); +void nsldapi_iostatus_free( LDAP *ld ); +int nsldapi_iostatus_interest_write( LDAP *ld, Sockbuf *sb ); +int nsldapi_iostatus_interest_read( LDAP *ld, Sockbuf *sb ); +int nsldapi_iostatus_interest_clear( LDAP *ld, Sockbuf *sb ); +int nsldapi_iostatus_is_read_ready( LDAP *ld, Sockbuf *sb ); +int nsldapi_iostatus_is_write_ready( LDAP *ld, Sockbuf *sb ); +int nsldapi_install_lber_extiofns( LDAP *ld, Sockbuf *sb ); +int nsldapi_install_compat_io_fns( LDAP *ld, struct ldap_io_fns *iofns ); + +/* + * if referral.c + */ +int nsldapi_parse_reference( LDAP *ld, BerElement *rber, char ***referralsp, + LDAPControl ***serverctrlsp ); + + +/* + * in result.c + */ +int ldap_msgdelete( LDAP *ld, int msgid ); +int nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted, + struct timeval *timeout, LDAPMessage **result ); +int nsldapi_wait_result( LDAP *ld, int msgid, int all, struct timeval *timeout, + LDAPMessage **result ); +int nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result ); + +/* + * in request.c + */ +int nsldapi_send_initial_request( LDAP *ld, int msgid, unsigned long msgtype, + char *dn, BerElement *ber ); +int nsldapi_send_pending_requests_nolock( LDAP *ld, LDAPConn *lc ); +int nsldapi_alloc_ber_with_options( LDAP *ld, BerElement **berp ); +void nsldapi_set_ber_options( LDAP *ld, BerElement *ber ); +int nsldapi_send_ber_message( LDAP *ld, Sockbuf *sb, BerElement *ber, + int freeit, int epipe_handler ); +int nsldapi_send_server_request( LDAP *ld, BerElement *ber, int msgid, + LDAPRequest *parentreq, LDAPServer *srvlist, LDAPConn *lc, + char *bindreqdn, int bind ); +LDAPConn *nsldapi_new_connection( LDAP *ld, LDAPServer **srvlistp, int use_ldsb, + int connect, int bind ); +LDAPRequest *nsldapi_find_request_by_msgid( LDAP *ld, int msgid ); +LDAPRequest *nsldapi_new_request( LDAPConn *lc, BerElement *ber, int msgid, + int expect_resp ); +void nsldapi_free_request( LDAP *ld, LDAPRequest *lr, int free_conn ); +void nsldapi_queue_request_nolock( LDAP *ld, LDAPRequest *lr ); +void nsldapi_free_connection( LDAP *ld, LDAPConn *lc, + LDAPControl **serverctrls, LDAPControl **clientctrls, + int force, int unbind ); +void nsldapi_dump_connection( LDAP *ld, LDAPConn *lconns, int all ); +void nsldapi_dump_requests_and_responses( LDAP *ld ); +int nsldapi_chase_v2_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, + int *totalcountp, int *chasingcountp ); +int nsldapi_chase_v3_refs( LDAP *ld, LDAPRequest *lr, char **refs, + int is_reference, int *totalcountp, int *chasingcountp ); +int nsldapi_append_referral( LDAP *ld, char **referralsp, char *s ); +void nsldapi_connection_lost_nolock( LDAP *ld, Sockbuf *sb ); + +#ifdef LDAP_SASLIO_HOOKS +/* + * in saslbind.c + */ +int nsldapi_sasl_is_inited(); +int nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg ); +int nsldapi_sasl_secprops( const char *in, + sasl_security_properties_t *secprops ); + +/* + * in saslio.c + */ +int nsldapi_sasl_install( LDAP *ld, LDAPConn *lconn ); +int nsldapi_sasl_open( LDAP *ld, LDAPConn *lconn, sasl_conn_t **ctx, sasl_ssf_t ssf ); + +#endif /* LDAP_SASLIO_HOOKS */ + +/* + * in search.c + */ +int nsldapi_build_search_req( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + int timelimit, int sizelimit, int msgid, BerElement **berp ); + +/* + * in unbind.c + */ +int ldap_ld_free( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls, int close ); +int nsldapi_send_unbind( LDAP *ld, Sockbuf *sb, LDAPControl **serverctrls, + LDAPControl **clientctrls ); + +#ifdef LDAP_DNS +/* + * in getdxbyname.c + */ +char **nsldapi_getdxbyname( char *domain ); + +#endif /* LDAP_DNS */ + +/* + * in unescape.c + */ +void nsldapi_hex_unescape( char *s ); + +/* + * in compat.c + */ +#ifdef hpux +char *nsldapi_compat_ctime_r( const time_t *clock, char *buf, int buflen ); +struct hostent *nsldapi_compat_gethostbyname_r( const char *name, + struct hostent *result, char *buffer, int buflen, int *h_errnop ); +#endif /* hpux */ + +/* + * in control.c + */ +int nsldapi_put_controls( LDAP *ld, LDAPControl **ctrls, int closeseq, + BerElement *ber ); +int nsldapi_get_controls( BerElement *ber, LDAPControl ***controlsp ); +int nsldapi_find_controls( BerElement *ber, LDAPControl ***controlsp ); +int nsldapi_dup_controls( LDAP *ld, LDAPControl ***ldctrls, + LDAPControl **newctrls ); +int nsldapi_build_control( char *oid, BerElement *ber, int freeber, + char iscritical, LDAPControl **ctrlp ); + + +/* + * in url.c + */ +int nsldapi_url_parse( const char *inurl, LDAPURLDesc **ludpp, + int dn_required ); + + +/* + * in charset.c + * + * If we ever want to expose character set translation functionality to + * users of libldap, all of these prototypes will need to be moved to ldap.h + */ +#ifdef STR_TRANSLATION +void ldap_set_string_translators( LDAP *ld, + BERTranslateProc encode_proc, BERTranslateProc decode_proc ); +int ldap_translate_from_t61( LDAP *ld, char **bufp, + unsigned long *lenp, int free_input ); +int ldap_translate_to_t61( LDAP *ld, char **bufp, + unsigned long *lenp, int free_input ); +void ldap_enable_translation( LDAP *ld, LDAPMessage *entry, + int enable ); +#ifdef LDAP_CHARSET_8859 +int ldap_t61_to_8859( char **bufp, unsigned long *buflenp, + int free_input ); +int ldap_8859_to_t61( char **bufp, unsigned long *buflenp, + int free_input ); +#endif /* LDAP_CHARSET_8859 */ +#endif /* STR_TRANSLATION */ + +/* + * in memcache.h + */ +int ldap_memcache_createkey( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + unsigned long *keyp ); +int ldap_memcache_result( LDAP *ld, int msgid, unsigned long key ); +int ldap_memcache_new( LDAP *ld, int msgid, unsigned long key, + const char *basedn ); +int ldap_memcache_append( LDAP *ld, int msgid, int bLast, LDAPMessage *result ); +int ldap_memcache_abandon( LDAP *ld, int msgid ); + +/* + * in sbind.c + */ +void nsldapi_handle_reconnect( LDAP *ld ); + +#endif /* _LDAPINT_H */ diff --git a/ldap/c-sdk/libraries/libldap/libldap.def b/ldap/c-sdk/libraries/libldap/libldap.def new file mode 100644 index 0000000000..dd79389a27 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/libldap.def @@ -0,0 +1,352 @@ +; +; ***** BEGIN LICENSE BLOCK ***** +; Version: MPL 1.1/GPL 2.0/LGPL 2. +; +; The contents of this file are subject to the Mozilla Public License Version +; 1.1 (the "License"); you may not use this file except in compliance with +; the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" basis, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +; for the specific language governing rights and limitations under the +; License. +; +; The Original Code is Mozilla Communicator client code. +; +; The Initial Developer of the Original Code is +; Netscape Communications Corporation. +; Portions created by the Initial Developer are Copyright (C) 1996-1999 +; the Initial Developer. All Rights Reserved. +; +; Contributor(s): +; +; Alternatively, the contents of this file may be used under the terms of +; either of the GNU General Public License Version 2 or later (the "GPL"), +; or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +; in which case the provisions of the GPL or the LGPL are applicable instead +; of those above. If you wish to allow use of your version of this file only +; under the terms of either the GPL or the LGPL, and not to allow others to +; use your version of this file under the terms of the MPL, indicate your +; decision by deleting the provisions above and replace them with the notice +; and other provisions required by the GPL or the LGPL. If you do not delete +; the provisions above, a recipient may use your version of this file under +; the terms of any one of the MPL, the GPL or the LGPL. +; +; ***** END LICENSE BLOCK ***** + +LIBRARY LDAP60 +VERSION 6.0 +HEAPSIZE 4096 + +EXPORTS +; exports list (generated by genexports.pl) +; + ldap_abandon + ldap_add + ldap_unbind + +; ldap_enable_cache +; ldap_disable_cache +; ldap_destroy_cache +; ldap_flush_cache +; ldap_uncache_entry + + ldap_compare + ldap_delete + ldap_result2error + ldap_err2string + ldap_modify + ldap_modrdn + ldap_open + ldap_first_entry + ldap_next_entry + ldap_get_dn + ldap_dn2ufn + ldap_first_attribute + ldap_next_attribute + ldap_get_values + ldap_get_values_len + ldap_count_entries + ldap_count_values + ldap_value_free + ldap_explode_dn + ldap_result + ldap_msgfree + ldap_search + ldap_add_s + ldap_bind_s + ldap_unbind_s + ldap_delete_s + ldap_modify_s + ldap_modrdn_s + ldap_search_s + ldap_search_st + ldap_compare_s + ldap_ufn_search_c + ldap_ufn_search_s + ldap_init_getfilter + ldap_getfilter_free + ldap_getfirstfilter + ldap_getnextfilter + ldap_simple_bind + ldap_simple_bind_s + ldap_bind + ldap_friendly_name + ldap_free_friendlymap + ldap_ufn_search_ct + +; ldap_set_cache_options +; ldap_uncache_request + + ldap_modrdn2 + ldap_modrdn2_s + ldap_ufn_setfilter + ldap_ufn_setprefix + ldap_ufn_timeout + ldap_init_getfilter_buf + ldap_setfilteraffixes + ldap_sort_entries + ldap_sort_values + ldap_sort_strcasecmp + ldap_count_values_len + ldap_name2template + ldap_value_free_len + +; manually comment and uncomment these five as necessary +; ldap_kerberos_bind1 +; ldap_kerberos_bind2 +; ldap_kerberos_bind_s +; ldap_kerberos_bind1_s +; ldap_kerberos_bind2_s + + ldap_init + ldap_is_dns_dn + ldap_explode_dns + ldap_mods_free + + ldap_is_ldap_url + ldap_free_urldesc + ldap_url_parse + ldap_url_search + ldap_url_search_s + ldap_url_search_st + ldap_set_rebind_proc + ber_skip_tag + ber_peek_tag + ber_get_int + ber_get_stringb + ber_get_stringa + ber_get_stringal + ber_get_bitstringa + ber_get_null + ber_get_boolean + ber_first_element + ber_next_element + ber_scanf + ber_bvfree + ber_bvecfree + ber_put_int + ber_put_ostring + ber_put_string + ber_put_bitstring + ber_put_null + ber_put_boolean + ber_start_seq + ber_start_set + ber_put_seq + ber_put_set + ber_printf + ber_read + ber_write + ber_free + ber_flush + ber_alloc + ber_dup + ber_get_next + ber_get_tag + ber_put_enum + der_alloc + ber_alloc_t + ber_bvdup + ber_init_w_nullchar + ber_reset + ber_get_option + ber_set_option + ber_sockbuf_alloc + ber_sockbuf_get_option + ber_sockbuf_set_option + ber_init + ber_flatten + ber_special_alloc + ber_special_free + ber_get_next_buffer + ber_err_print + ber_sockbuf_free + ber_get_next_buffer_ext + ber_svecfree + ber_get_buf_datalen + ber_get_buf_databegin + ber_stack_init + ber_sockbuf_free_data + + ldap_memfree + ldap_ber_free + + ldap_init_searchprefs + ldap_init_searchprefs_buf + ldap_free_searchprefs + ldap_first_searchobj + ldap_next_searchobj + ldap_build_filter + + ldap_init_templates + ldap_init_templates_buf + ldap_free_templates + ldap_first_disptmpl + ldap_next_disptmpl + ldap_oc2template + ldap_tmplattrs + ldap_first_tmplrow + ldap_next_tmplrow + ldap_first_tmplcol + ldap_next_tmplcol + ldap_entry2text_search + ldap_entry2text + ldap_vals2text + ldap_entry2html + ldap_entry2html_search + ldap_vals2html + ldap_tmplerr2string + ldap_set_option + ldap_get_option + ldap_charray_merge + ldap_get_lderrno + ldap_set_lderrno + ldap_perror + ldap_set_filter_additions + ldap_create_filter + ldap_version + ldap_multisort_entries + ldap_msgid + ldap_explode_rdn + ldap_msgtype + ldap_cache_flush + ldap_str2charray + ldap_charray_add + ldap_charray_dup + ldap_charray_free + +; Windows ordinals 450-469 are reserved for SSL routines + + ldap_charray_inlist + ldap_charray_position + ldap_rename + ldap_rename_s + ldap_utf8len + ldap_utf8next + ldap_utf8prev + ldap_utf8copy + ldap_utf8characters + ldap_utf8strtok_r + ldap_utf8isalnum + ldap_utf8isalpha + ldap_utf8isdigit + ldap_utf8isxdigit + ldap_utf8isspace + ldap_control_free + ldap_controls_free + ldap_sasl_bind + ldap_sasl_bind_s + ldap_parse_sasl_bind_result + ldap_sasl_interactive_bind_s + ldap_sasl_interactive_bind_ext_s +; LDAPv3 simple paging controls are not supported by Netscape at this time. +; 490 ldap_create_page_control +; 491 ldap_parse_page_control + ldap_create_sort_control + ldap_parse_sort_control +; an LDAPv3 language control was proposed but then retracted. +; 494 ldap_create_language_control + ldap_get_lang_values + ldap_get_lang_values_len + ldap_free_sort_keylist + ldap_create_sort_keylist + ldap_utf8getcc + ldap_get_entry_controls + ldap_create_persistentsearch_control + ldap_parse_entrychange_control + ldap_parse_result + ldap_parse_extended_result + ldap_parse_reference + ldap_abandon_ext + ldap_add_ext + ldap_add_ext_s + ldap_modify_ext + ldap_modify_ext_s + ldap_first_message + ldap_next_message + ldap_compare_ext + ldap_compare_ext_s + ldap_delete_ext + ldap_delete_ext_s + ldap_search_ext + ldap_search_ext_s + ldap_extended_operation + ldap_extended_operation_s + ldap_first_reference + ldap_next_reference + ldap_count_references + ldap_count_messages + ldap_create_virtuallist_control + ldap_parse_virtuallist_control + ldap_create_proxyauth_control + ldap_unbind_ext + ldap_x_hostlist_first + ldap_x_hostlist_next + ldap_x_hostlist_statusfree + ldap_x_malloc + ldap_x_calloc + ldap_x_realloc + ldap_x_free +; + ldap_create_proxiedauth_control +; + ldap_create_geteffectiveRights_control +; + ldap_find_control +; + ldap_url_parse_no_defaults +; + ldap_create_userstatus_control + ldap_parse_userstatus_control +; + ldap_create_passwordpolicy_control + ldap_create_passwordpolicy_control_ext + ldap_parse_passwordpolicy_control + ldap_parse_passwordpolicy_control_ext + ldap_passwordpolicy_err2txt +; + ldap_passwd + ldap_parse_passwd + ldap_passwd_s +; + ldap_delete_result_entry + ldap_add_result_entry +; + ldap_whoami + ldap_parse_whoami + ldap_whoami_s +; + ldap_create_authzid_control + ldap_parse_authzid_control +; + ldap_memcache_init + ldap_memcache_set + ldap_memcache_get + ldap_memcache_flush + ldap_memcache_destroy + ldap_memcache_update + ldap_keysort_entries +; +; end of generated exports list. diff --git a/ldap/c-sdk/libraries/libldap/memcache.c b/ldap/c-sdk/libraries/libldap/memcache.c new file mode 100644 index 0000000000..78943408aa --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/memcache.c @@ -0,0 +1,2244 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * + * memcache.c - routines that implement an in-memory cache. + * + * To Do: 1) ber_dup_ext(). + * 2) referrals and reference? + */ + +#include +#include "ldap-int.h" + +/* + * Extra size allocated to BerElement. + * XXXmcs: must match EXBUFSIZ in liblber/io.c? + */ +#define EXTRA_SIZE 1024 + +/* Mode constants for function memcache_access() */ +#define MEMCACHE_ACCESS_ADD 0 +#define MEMCACHE_ACCESS_APPEND 1 +#define MEMCACHE_ACCESS_APPEND_LAST 2 +#define MEMCACHE_ACCESS_FIND 3 +#define MEMCACHE_ACCESS_DELETE 4 +#define MEMCACHE_ACCESS_DELETE_ALL 5 +#define MEMCACHE_ACCESS_UPDATE 6 +#define MEMCACHE_ACCESS_FLUSH 7 +#define MEMCACHE_ACCESS_FLUSH_ALL 8 +#define MEMCACHE_ACCESS_FLUSH_LRU 9 +#define MEMCACHE_ACCESS_FLUSH_RESULTS 10 + +/* Mode constants for function memcache_adj_size */ +#define MEMCACHE_SIZE_DEDUCT 0 +#define MEMCACHE_SIZE_ADD 1 + +#define MEMCACHE_SIZE_ENTRIES 1 +#define MEMCACHE_SIZE_NON_ENTRIES 2 + +/* Size used for calculation if given size of cache is 0 */ +#define MEMCACHE_DEF_SIZE 131072 /* 128K bytes */ + +/* Index into different list structure */ +#define LIST_TTL 0 +#define LIST_LRU 1 +#define LIST_TMP 2 +#define LIST_TOTAL 3 + +/* Macros to make code more readable */ +#define NSLDAPI_VALID_MEMCACHE_POINTER( cp ) ( (cp) != NULL ) +#define NSLDAPI_STR_NONNULL( s ) ( (s) ? (s) : "" ) +#define NSLDAPI_SAFE_STRLEN( s ) ( (s) ? strlen((s)) + 1 : 1 ) + +/* Macros dealing with mutex */ +#define LDAP_MEMCACHE_MUTEX_LOCK( c ) \ + if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_lock ) { \ + ((c)->ldmemc_lock_fns).ltf_mutex_lock( (c)->ldmemc_lock ); \ + } + +#define LDAP_MEMCACHE_MUTEX_UNLOCK( c ) \ + if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_unlock ) { \ + ((c)->ldmemc_lock_fns).ltf_mutex_unlock( (c)->ldmemc_lock ); \ + } + +#define LDAP_MEMCACHE_MUTEX_ALLOC( c ) \ + ((c) && ((c)->ldmemc_lock_fns).ltf_mutex_alloc ? \ + ((c)->ldmemc_lock_fns).ltf_mutex_alloc() : NULL) + +#define LDAP_MEMCACHE_MUTEX_FREE( c ) \ + if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_free ) { \ + ((c)->ldmemc_lock_fns).ltf_mutex_free( (c)->ldmemc_lock ); \ + } + +/* Macros used for triming unnecessary spaces in a basedn */ +#define NSLDAPI_IS_SPACE( c ) \ + (((c) == ' ') || ((c) == '\t') || ((c) == '\n')) + +#define NSLDAPI_IS_SEPARATER( c ) \ + ((c) == ',') + +/* Hash table callback function pointer definition */ +typedef int (*HashFuncPtr)(int table_size, void *key); +typedef int (*PutDataPtr)(void **ppTableData, void *key, void *pData); +typedef int (*GetDataPtr)(void *pTableData, void *key, void **ppData); +typedef int (*RemoveDataPtr)(void **ppTableData, void *key, void **ppData); +typedef int (*MiscFuncPtr)(void **ppTableData, void *key, void *pData); +typedef void (*ClrTableNodePtr)(void **ppTableData, void *pData); + +/* Structure of a node in a hash table */ +typedef struct HashTableNode_struct { + void *pData; +} HashTableNode; + +/* Structure of a hash table */ +typedef struct HashTable_struct { + HashTableNode *table; + int size; + HashFuncPtr hashfunc; + PutDataPtr putdata; + GetDataPtr getdata; + MiscFuncPtr miscfunc; + RemoveDataPtr removedata; + ClrTableNodePtr clrtablenode; +} HashTable; + +/* Structure uniquely identifies a search request */ +typedef struct ldapmemcacheReqId_struct { + LDAP *ldmemcrid_ld; + int ldmemcrid_msgid; +} ldapmemcacheReqId; + +/* Structure representing a ldap handle associated to memcache */ +typedef struct ldapmemcacheld_struct { + LDAP *ldmemcl_ld; + struct ldapmemcacheld_struct *ldmemcl_next; +} ldapmemcacheld; + +/* Structure representing header of a search result */ +typedef struct ldapmemcacheRes_struct { + char *ldmemcr_basedn; + unsigned long ldmemcr_crc_key; + unsigned long ldmemcr_resSize; + unsigned long ldmemcr_timestamp; + LDAPMessage *ldmemcr_resHead; + LDAPMessage *ldmemcr_resTail; + ldapmemcacheReqId ldmemcr_req_id; + struct ldapmemcacheRes_struct *ldmemcr_next[LIST_TOTAL]; + struct ldapmemcacheRes_struct *ldmemcr_prev[LIST_TOTAL]; + struct ldapmemcacheRes_struct *ldmemcr_htable_next; +} ldapmemcacheRes; + +/* Structure for memcache statistics */ +typedef struct ldapmemcacheStats_struct { + unsigned long ldmemcstat_tries; + unsigned long ldmemcstat_hits; +} ldapmemcacheStats; + +/* Structure of a memcache object */ +struct ldapmemcache { + unsigned long ldmemc_ttl; + unsigned long ldmemc_size; + unsigned long ldmemc_size_used; + unsigned long ldmemc_size_entries; + char **ldmemc_basedns; + void *ldmemc_lock; + ldapmemcacheld *ldmemc_lds; + HashTable *ldmemc_resTmp; + HashTable *ldmemc_resLookup; + ldapmemcacheRes *ldmemc_resHead[LIST_TOTAL]; + ldapmemcacheRes *ldmemc_resTail[LIST_TOTAL]; + struct ldap_thread_fns ldmemc_lock_fns; + ldapmemcacheStats ldmemc_stats; +}; + +/* Function prototypes */ +static int memcache_exist(LDAP *ld); +static int memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg); +static int memcache_compare_dn(const char *main_dn, const char *dn, int scope); +static int memcache_dup_message(LDAPMessage *res, int msgid, int fromcache, + LDAPMessage **ppResCopy, unsigned long *pSize); +static BerElement* memcache_ber_dup(BerElement* pBer, unsigned long *pSize); + +static void memcache_trim_basedn_spaces(char *basedn); +static int memcache_validate_basedn(LDAPMemCache *cache, const char *basedn); +static int memcache_get_ctrls_len(LDAPControl **ctrls); +static void memcache_append_ctrls(char *buf, LDAPControl **serverCtrls, + LDAPControl **clientCtrls); +static int memcache_adj_size(LDAPMemCache *cache, unsigned long size, + int usageFlags, int bAdd); +static int memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes); +static int memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes, + unsigned long curTime); +static int memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, + int index); +static int memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg, + unsigned long size); +static int memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, + int index); +static int memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes); +static int memcache_add(LDAP *ld, unsigned long key, int msgid, + const char *basedn); +static int memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes); +static int memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes); +static int memcache_remove(LDAP *ld, int msgid); +#if 0 /* function not used */ +static int memcache_remove_all(LDAP *ld); +#endif /* 0 */ +static int memcache_access(LDAPMemCache *cache, int mode, + void *pData1, void *pData2, void *pData3); +static void memcache_flush(LDAPMemCache *cache, char *dn, int scope, + int flushresults); +#ifdef LDAP_DEBUG +static void memcache_print_list( LDAPMemCache *cache, int index ); +static void memcache_report_statistics( LDAPMemCache *cache ); +#endif /* LDAP_DEBUG */ + +static int htable_calculate_size(int sizelimit); +static int htable_sizeinbytes(HashTable *pTable); +static int htable_put(HashTable *pTable, void *key, void *pData); +static int htable_get(HashTable *pTable, void *key, void **ppData); +static int htable_misc(HashTable *pTable, void *key, void *pData); +static int htable_remove(HashTable *pTable, void *key, void **ppData); +static int htable_removeall(HashTable *pTable, void *pData); +static int htable_create(int size_limit, HashFuncPtr hashf, + PutDataPtr putDataf, GetDataPtr getDataf, + RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef, + MiscFuncPtr miscOpf, HashTable **ppTable); +static int htable_free(HashTable *pTable); + +static int msgid_hashf(int table_size, void *key); +static int msgid_putdata(void **ppTableData, void *key, void *pData); +static int msgid_getdata(void *pTableData, void *key, void **ppData); +static int msgid_removedata(void **ppTableData, void *key, void **ppData); +static int msgid_clear_ld_items(void **ppTableData, void *key, void *pData); +static void msgid_clearnode(void **ppTableData, void *pData); + +static int attrkey_hashf(int table_size, void *key); +static int attrkey_putdata(void **ppTableData, void *key, void *pData); +static int attrkey_getdata(void *pTableData, void *key, void **ppData); +static int attrkey_removedata(void **ppTableData, void *key, void **ppData); +static void attrkey_clearnode(void **ppTableData, void *pData); + +static unsigned long crc32_convert(char *buf, int len); + +/* Create a memcache object. */ +int +LDAP_CALL +ldap_memcache_init( unsigned long ttl, unsigned long size, + char **baseDNs, struct ldap_thread_fns *thread_fns, + LDAPMemCache **cachep ) +{ + unsigned long total_size = 0; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init\n", 0, 0, 0 ); + + if ( cachep == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + if ((*cachep = (LDAPMemCache*)NSLDAPI_CALLOC(1, + sizeof(LDAPMemCache))) == NULL) { + return ( LDAP_NO_MEMORY ); + } + + total_size += sizeof(LDAPMemCache); + + (*cachep)->ldmemc_ttl = ttl; + (*cachep)->ldmemc_size = size; + (*cachep)->ldmemc_lds = NULL; + + /* Non-zero default size needed for calculating size of hash tables */ + size = (size ? size : MEMCACHE_DEF_SIZE); + + if (thread_fns) { + memcpy(&((*cachep)->ldmemc_lock_fns), thread_fns, + sizeof(struct ldap_thread_fns)); + } else { + memset(&((*cachep)->ldmemc_lock_fns), 0, + sizeof(struct ldap_thread_fns)); + } + + (*cachep)->ldmemc_lock = LDAP_MEMCACHE_MUTEX_ALLOC( *cachep ); + + /* Cache basedns */ + if (baseDNs != NULL) { + + int i; + + for (i = 0; baseDNs[i]; i++) { + ; + } + + (*cachep)->ldmemc_basedns = (char**)NSLDAPI_CALLOC(i + 1, + sizeof(char*)); + + if ((*cachep)->ldmemc_basedns == NULL) { + ldap_memcache_destroy(*cachep); + *cachep = NULL; + return ( LDAP_NO_MEMORY ); + } + + total_size += (i + 1) * sizeof(char*); + + for (i = 0; baseDNs[i]; i++) { + (*cachep)->ldmemc_basedns[i] = nsldapi_strdup(baseDNs[i]); + total_size += strlen(baseDNs[i]) + 1; + } + + (*cachep)->ldmemc_basedns[i] = NULL; + } + + /* Create hash table for temporary cache */ + if (htable_create(size, msgid_hashf, msgid_putdata, msgid_getdata, + msgid_removedata, msgid_clearnode, msgid_clear_ld_items, + &((*cachep)->ldmemc_resTmp)) != LDAP_SUCCESS) { + ldap_memcache_destroy(*cachep); + *cachep = NULL; + return( LDAP_NO_MEMORY ); + } + + total_size += htable_sizeinbytes((*cachep)->ldmemc_resTmp); + + /* Create hash table for primary cache */ + if (htable_create(size, attrkey_hashf, attrkey_putdata, + attrkey_getdata, attrkey_removedata, attrkey_clearnode, + NULL, &((*cachep)->ldmemc_resLookup)) != LDAP_SUCCESS) { + ldap_memcache_destroy(*cachep); + *cachep = NULL; + return( LDAP_NO_MEMORY ); + } + + total_size += htable_sizeinbytes((*cachep)->ldmemc_resLookup); + + /* See if there is enough room so far */ + if (memcache_adj_size(*cachep, total_size, MEMCACHE_SIZE_NON_ENTRIES, + MEMCACHE_SIZE_ADD) != LDAP_SUCCESS) { + ldap_memcache_destroy(*cachep); + *cachep = NULL; + return( LDAP_SIZELIMIT_EXCEEDED ); + } + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init new cache 0x%p\n", + *cachep, 0, 0 ); + + return( LDAP_SUCCESS ); +} + +/* Associates a ldap handle to a memcache object. */ +int +LDAP_CALL +ldap_memcache_set( LDAP *ld, LDAPMemCache *cache ) +{ + int nRes = LDAP_SUCCESS; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_set\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) + return( LDAP_PARAM_ERROR ); + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + + if (ld->ld_memcache != cache) { + + LDAPMemCache *c = ld->ld_memcache; + ldapmemcacheld *pCur = NULL; + ldapmemcacheld *pPrev = NULL; + + /* First dissociate handle from old cache */ + + LDAP_MEMCACHE_MUTEX_LOCK( c ); + + pCur = (c ? c->ldmemc_lds : NULL); + for (; pCur; pCur = pCur->ldmemcl_next) { + if (pCur->ldmemcl_ld == ld) + break; + pPrev = pCur; + } + + if (pCur) { + + ldapmemcacheReqId reqid; + + reqid.ldmemcrid_ld = ld; + reqid.ldmemcrid_msgid = -1; + htable_misc(c->ldmemc_resTmp, (void*)&reqid, (void*)c); + + if (pPrev) + pPrev->ldmemcl_next = pCur->ldmemcl_next; + else + c->ldmemc_lds = pCur->ldmemcl_next; + NSLDAPI_FREE(pCur); + pCur = NULL; + + memcache_adj_size(c, sizeof(ldapmemcacheld), + MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT); + } + + LDAP_MEMCACHE_MUTEX_UNLOCK( c ); + + ld->ld_memcache = NULL; + + /* Exit if no new cache is specified */ + if (cache == NULL) { + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + return( LDAP_SUCCESS ); + } + + /* Then associate handle with new cache */ + + LDAP_MEMCACHE_MUTEX_LOCK( cache ); + + if ((nRes = memcache_adj_size(cache, sizeof(ldapmemcacheld), + MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_ADD)) != LDAP_SUCCESS) { + LDAP_MEMCACHE_MUTEX_UNLOCK( cache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + return nRes; + } + + pCur = (ldapmemcacheld*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheld)); + if (pCur == NULL) { + memcache_adj_size(cache, sizeof(ldapmemcacheld), + MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT); + nRes = LDAP_NO_MEMORY; + } else { + pCur->ldmemcl_ld = ld; + pCur->ldmemcl_next = cache->ldmemc_lds; + cache->ldmemc_lds = pCur; + ld->ld_memcache = cache; + } + + LDAP_MEMCACHE_MUTEX_UNLOCK( cache ); + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return nRes; +} + +/* Retrieves memcache with which the ldap handle has been associated. */ +int +LDAP_CALL +ldap_memcache_get( LDAP *ld, LDAPMemCache **cachep ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_get ld: 0x%p\n", ld, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || cachep == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + *cachep = ld->ld_memcache; + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return( LDAP_SUCCESS ); +} + +/* + * Function that stays inside libldap and proactively expires items from + * the given cache. This should be called from a newly created thread since + * it will not return until after ldap_memcache_destroy() is called. + */ +void +LDAP_CALL +ldap_memcache_update( LDAPMemCache *cache ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_update: cache 0x%p\n", + cache, 0, 0 ); + + if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) { + return; + } + + LDAP_MEMCACHE_MUTEX_LOCK( cache ); + memcache_access(cache, MEMCACHE_ACCESS_UPDATE, NULL, NULL, NULL); + LDAP_MEMCACHE_MUTEX_UNLOCK( cache ); +} + +/* Removes specified entries from given memcache. Only clears out search + results that included search entries. */ +void +LDAP_CALL +ldap_memcache_flush( LDAPMemCache *cache, char *dn, int scope ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_flush( cache: 0x%p, dn: %s, scope: %d)\n", + cache, ( dn == NULL ) ? "(null)" : dn, scope ); + memcache_flush(cache, dn, scope, 0 /* Don't use result flush mode */); +} + +/* Removes specified entries from given memcache, including search + results that returned no entries. */ +void +LDAP_CALL +ldap_memcache_flush_results( LDAPMemCache *cache, char *dn, int scope ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_flush_results( cache: 0x%p, dn: %s, scope: %d)\n", + cache, ( dn == NULL ) ? "(null)" : dn, scope ); + memcache_flush(cache, dn, scope, 1 /* Use result flush mode */); +} + +/* Destroys the given memcache. */ +void +LDAP_CALL +ldap_memcache_destroy( LDAPMemCache *cache ) +{ + int i = 0; + unsigned long size = sizeof(LDAPMemCache); + ldapmemcacheld *pNode = NULL, *pNextNode = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_destroy( 0x%p )\n", + cache, 0, 0 ); + + if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) { + return; + } + + /* Dissociate all ldap handes from this cache. */ + LDAP_MEMCACHE_MUTEX_LOCK( cache ); + + for (pNode = cache->ldmemc_lds; pNode; pNode = pNextNode, i++) { + LDAP_MUTEX_LOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK ); + cache->ldmemc_lds = pNode->ldmemcl_next; + pNode->ldmemcl_ld->ld_memcache = NULL; + LDAP_MUTEX_UNLOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK ); + pNextNode = pNode->ldmemcl_next; + NSLDAPI_FREE(pNode); + } + + size += i * sizeof(ldapmemcacheld); + + LDAP_MEMCACHE_MUTEX_UNLOCK( cache ); + + /* Free array of basedns */ + if (cache->ldmemc_basedns) { + for (i = 0; cache->ldmemc_basedns[i]; i++) { + size += strlen(cache->ldmemc_basedns[i]) + 1; + NSLDAPI_FREE(cache->ldmemc_basedns[i]); + } + size += (i + 1) * sizeof(char*); + NSLDAPI_FREE(cache->ldmemc_basedns); + } + + /* Free hash table used for temporary cache */ + if (cache->ldmemc_resTmp) { + size += htable_sizeinbytes(cache->ldmemc_resTmp); + memcache_access(cache, MEMCACHE_ACCESS_DELETE_ALL, NULL, NULL, NULL); + htable_free(cache->ldmemc_resTmp); + } + + /* Free hash table used for primary cache */ + if (cache->ldmemc_resLookup) { + size += htable_sizeinbytes(cache->ldmemc_resLookup); + memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL); + htable_free(cache->ldmemc_resLookup); + } + + memcache_adj_size(cache, size, MEMCACHE_SIZE_NON_ENTRIES, + MEMCACHE_SIZE_DEDUCT); + + LDAP_MEMCACHE_MUTEX_FREE( cache ); + + NSLDAPI_FREE(cache); +} + +/************************* Internal API Functions ****************************/ + +/* Creates an integer key by applying the Cyclic Reduntency Check algorithm on + a long string formed by concatenating all the search parameters plus the + current bind DN. The key is used in the cache for looking up cached + entries. It is assumed that the CRC algorithm will generate + different integers from different byte strings. */ +int +ldap_memcache_createkey(LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, + int attrsonly, LDAPControl **serverctrls, + LDAPControl **clientctrls, unsigned long *keyp) +{ + int nRes, i, j, i_smallest; + int len; + int defport; + char buf[50]; + char *tmp, *defhost, *binddn, *keystr, *tmpbase; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (keyp == NULL) ) + return( LDAP_PARAM_ERROR ); + + *keyp = 0; + + if (!memcache_exist(ld)) + return( LDAP_LOCAL_ERROR ); + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache ); + nRes = memcache_validate_basedn(ld->ld_memcache, base); + LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + if (nRes != LDAP_SUCCESS) + return nRes; + + defhost = NSLDAPI_STR_NONNULL(ld->ld_defhost); + defport = ld->ld_defport; + tmpbase = nsldapi_strdup(NSLDAPI_STR_NONNULL(base)); + memcache_trim_basedn_spaces(tmpbase); + + if ((binddn = nsldapi_get_binddn(ld)) == NULL) + binddn = ""; + + sprintf(buf, "%i\n%i\n%i\n", defport, scope, (attrsonly ? 1 : 0)); + len = NSLDAPI_SAFE_STRLEN(buf) + NSLDAPI_SAFE_STRLEN(tmpbase) + + NSLDAPI_SAFE_STRLEN(filter) + NSLDAPI_SAFE_STRLEN(defhost) + + NSLDAPI_SAFE_STRLEN(binddn); + + if (attrs) { + for (i = 0; attrs[i]; i++) { + + for (i_smallest = j = i; attrs[j]; j++) { + if (strcasecmp(attrs[i_smallest], attrs[j]) > 0) + i_smallest = j; + } + + if (i != i_smallest) { + tmp = attrs[i]; + attrs[i] = attrs[i_smallest]; + attrs[i_smallest] = tmp; + } + + len += NSLDAPI_SAFE_STRLEN(attrs[i]); + } + } else { + len += 1; + } + + len += memcache_get_ctrls_len(serverctrls) + + memcache_get_ctrls_len(clientctrls) + 1; + + if ((keystr = (char*)NSLDAPI_CALLOC(len, sizeof(char))) == NULL) { + NSLDAPI_FREE(defhost); + return( LDAP_NO_MEMORY ); + } + + sprintf(keystr, "%s\n%s\n%s\n%s\n%s\n", binddn, tmpbase, + NSLDAPI_STR_NONNULL(defhost), NSLDAPI_STR_NONNULL(filter), + NSLDAPI_STR_NONNULL(buf)); + + if (attrs) { + for (i = 0; attrs[i]; i++) { + strcat(keystr, NSLDAPI_STR_NONNULL(attrs[i])); + strcat(keystr, "\n"); + } + } else { + strcat(keystr, "\n"); + } + + for (tmp = keystr; *tmp; + *tmp += (*tmp >= 'a' && *tmp <= 'z' ? 'A'-'a' : 0), tmp++) { + ; + } + + memcache_append_ctrls(keystr, serverctrls, clientctrls); + + /* CRC algorithm */ + *keyp = crc32_convert(keystr, len); + + NSLDAPI_FREE(keystr); + NSLDAPI_FREE(tmpbase); + + return LDAP_SUCCESS; +} + +/* Searches the cache for the right cached entries, and if found, attaches + them to the given ldap handle. This function relies on locking by the + caller. */ +int +ldap_memcache_result(LDAP *ld, int msgid, unsigned long key) +{ + int nRes; + LDAPMessage *pMsg = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_result( ld: 0x%p, msgid: %d, key: 0x%8.8lx)\n", + ld, msgid, key ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) { + return( LDAP_PARAM_ERROR ); + } + + if (!memcache_exist(ld)) { + return( LDAP_LOCAL_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache ); + + /* Search the cache and append the results to ld if found */ + ++ld->ld_memcache->ldmemc_stats.ldmemcstat_tries; + if ((nRes = memcache_search(ld, key, &pMsg)) == LDAP_SUCCESS) { + nRes = memcache_add_to_ld(ld, msgid, pMsg); + ++ld->ld_memcache->ldmemc_stats.ldmemcstat_hits; + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_result: key 0x%8.8lx found in cache\n", + key, 0, 0 ); + } else { + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_result: key 0x%8.8lx not found in cache\n", + key, 0, 0 ); + } + +#ifdef LDAP_DEBUG + memcache_print_list( ld->ld_memcache, LIST_LRU ); + memcache_report_statistics( ld->ld_memcache ); +#endif /* LDAP_DEBUG */ + + LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return nRes; +} + +/* Creates a new header in the cache so that entries arriving from the + directory server can later be cached under the header. */ +int +ldap_memcache_new(LDAP *ld, int msgid, unsigned long key, const char *basedn) +{ + int nRes; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + + if (!memcache_exist(ld)) { + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + return( LDAP_LOCAL_ERROR ); + } + + LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache ); + nRes = memcache_add(ld, key, msgid, basedn); + LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return nRes; +} + +/* Appends a chain of entries to an existing cache header. Parameter "bLast" + indicates whether there will be more entries arriving for the search in + question. */ +int +ldap_memcache_append(LDAP *ld, int msgid, int bLast, LDAPMessage *result) +{ + int nRes = LDAP_SUCCESS; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_append( ld: 0x%p, ", ld, 0, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, "msgid %d, bLast: %d, result: 0x%p)\n", + msgid, bLast, result ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || !result ) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + + if (!memcache_exist(ld)) { + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + return( LDAP_LOCAL_ERROR ); + } + + LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache ); + + if (!bLast) + nRes = memcache_append(ld, msgid, result); + else + nRes = memcache_append_last(ld, msgid, result); + + LDAPDebug( LDAP_DEBUG_TRACE, + "ldap_memcache_append: %s result for msgid %d\n", + ( nRes == LDAP_SUCCESS ) ? "added" : "failed to add", msgid , 0 ); + + LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return nRes; +} + +/* Removes partially cached results for a search as a result of calling + ldap_abandon() by the client. */ +int +ldap_memcache_abandon(LDAP *ld, int msgid) +{ + int nRes; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK ); + + if (!memcache_exist(ld)) { + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + return( LDAP_LOCAL_ERROR ); + } + + LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache ); + nRes = memcache_remove(ld, msgid); + LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache ); + LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK ); + + return nRes; +} + +/*************************** helper functions *******************************/ + +/* Removes extraneous spaces in a basedn so that basedns differ by only those + spaces will be treated as equal. Extraneous spaces are those that + precedes the basedn and those that follow a comma. */ +/* + * XXXmcs: this is a bit too agressive... we need to deal with the fact that + * commas and spaces may be quoted, in which case it is wrong to remove them. + */ +static void +memcache_trim_basedn_spaces(char *basedn) +{ + char *pRead, *pWrite; + + if (!basedn) + return; + + for (pWrite = pRead = basedn; *pRead; ) { + for (; *pRead && NSLDAPI_IS_SPACE(*pRead); pRead++) { + ; + } + for (; *pRead && !NSLDAPI_IS_SEPARATER(*pRead); + *(pWrite++) = *(pRead++)) { + ; + } + *(pWrite++) = (*pRead ? *(pRead++) : *pRead); + } +} + +/* Verifies whether the results of a search should be cached or not by + checking if the search's basedn falls under any of the basedns for which + the memcache is responsible. */ +static int +memcache_validate_basedn(LDAPMemCache *cache, const char *basedn) +{ + int i; + + if ( cache->ldmemc_basedns == NULL ) { + return( LDAP_SUCCESS ); + } + +#if 1 + if (basedn == NULL) { + basedn = ""; + } +#else + /* XXXmcs: I do not understand this code... */ + if (basedn == NULL) + return (cache->ldmemc_basedns && cache->ldmemc_basedns[0] ? + LDAP_OPERATIONS_ERROR : LDAP_SUCCESS); + } +#endif + + for (i = 0; cache->ldmemc_basedns[i]; i++) { + if (memcache_compare_dn(basedn, cache->ldmemc_basedns[i], + LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) { + return( LDAP_SUCCESS ); + } + } + + return( LDAP_OPERATIONS_ERROR ); +} + +/* Calculates the length of the buffer needed to concatenate the contents of + a ldap control. */ +static int +memcache_get_ctrls_len(LDAPControl **ctrls) +{ + int len = 0, i; + + if (ctrls) { + for (i = 0; ctrls[i]; i++) { + len += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) + + (ctrls[i]->ldctl_value).bv_len + 4; + } + } + + return len; +} + +/* Contenates the contents of client and server controls to a buffer. */ +static void +memcache_append_ctrls(char *buf, LDAPControl **serverCtrls, + LDAPControl **clientCtrls) +{ + int i, j; + char *pCh = buf + strlen(buf); + LDAPControl **ctrls; + + for (j = 0; j < 2; j++) { + + if ((ctrls = (j ? clientCtrls : serverCtrls)) == NULL) + continue; + + for (i = 0; ctrls[i]; i++) { + sprintf(pCh, "%s\n", NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)); + pCh += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) + 1; + if ((ctrls[i]->ldctl_value).bv_len > 0) { + memcpy(pCh, (ctrls[i]->ldctl_value).bv_val, + (ctrls[i]->ldctl_value).bv_len); + pCh += (ctrls[i]->ldctl_value).bv_len; + } + sprintf(pCh, "\n%i\n", (ctrls[i]->ldctl_iscritical ? 1 : 0)); + pCh += 3; + } + } +} + +/* Increases or decreases the size (in bytes) the given memcache currently + uses. If the size goes over the limit, the function returns an error. */ +static int +memcache_adj_size(LDAPMemCache *cache, unsigned long size, + int usageFlags, int bAdd) +{ + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_adj_size: attempting to %s %ld %s bytes...\n", + bAdd ? "add" : "remove", size, + ( usageFlags & MEMCACHE_SIZE_ENTRIES ) ? "entry" : "non-entry" ); + + if (bAdd) { + cache->ldmemc_size_used += size; + if ((cache->ldmemc_size > 0) && + (cache->ldmemc_size_used > cache->ldmemc_size)) { + + if (size > cache->ldmemc_size_entries) { + cache->ldmemc_size_used -= size; + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_adj_size: failed (size > size_entries %ld).\n", + cache->ldmemc_size_entries, 0, 0 ); + return( LDAP_SIZELIMIT_EXCEEDED ); + } + + while (cache->ldmemc_size_used > cache->ldmemc_size) { + if (memcache_access(cache, MEMCACHE_ACCESS_FLUSH_LRU, + NULL, NULL, NULL) != LDAP_SUCCESS) { + cache->ldmemc_size_used -= size; + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_adj_size: failed (LRU flush failed).\n", + 0, 0, 0 ); + return( LDAP_SIZELIMIT_EXCEEDED ); + } + } + } + if (usageFlags & MEMCACHE_SIZE_ENTRIES) + cache->ldmemc_size_entries += size; + } else { + cache->ldmemc_size_used -= size; + assert(cache->ldmemc_size_used >= 0); + if (usageFlags & MEMCACHE_SIZE_ENTRIES) + cache->ldmemc_size_entries -= size; + } + +#ifdef LDAP_DEBUG + if ( cache->ldmemc_size == 0 ) { /* no size limit */ + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_adj_size: succeeded (new size: %ld bytes).\n", + cache->ldmemc_size_used, 0, 0 ); + } else { + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_adj_size: succeeded (new size: %ld bytes, " + "free space: %ld bytes).\n", cache->ldmemc_size_used, + cache->ldmemc_size - cache->ldmemc_size_used, 0 ); + } +#endif /* LDAP_DEBUG */ + + return( LDAP_SUCCESS ); +} + +/* Searches the cache for results for a particular search identified by + parameter "key", which was generated ldap_memcache_createkey(). */ +static int +memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes) +{ + int nRes; + ldapmemcacheRes *pRes; + + *ppRes = NULL; + + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + nRes = memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_FIND, + (void*)&key, (void*)(&pRes), NULL); + + if (nRes != LDAP_SUCCESS) + return nRes; + + *ppRes = pRes->ldmemcr_resHead; + assert((pRes->ldmemcr_req_id).ldmemcrid_msgid == -1); + + return( LDAP_SUCCESS ); +} + +/* Adds a new header into the cache as a place holder for entries + arriving later. */ +static int +memcache_add(LDAP *ld, unsigned long key, int msgid, + const char *basedn) +{ + ldapmemcacheReqId reqid; + + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + reqid.ldmemcrid_msgid = msgid; + reqid.ldmemcrid_ld = ld; + + return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_ADD, + (void*)&key, (void*)&reqid, (void*)basedn); +} + +/* Appends search entries arriving from the dir server to the cache. */ +static int +memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes) +{ + ldapmemcacheReqId reqid; + + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + reqid.ldmemcrid_msgid = msgid; + reqid.ldmemcrid_ld = ld; + + return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND, + (void*)&reqid, (void*)pRes, NULL); +} + +/* Same as memcache_append(), but the entries being appended are the + last from the dir server. Once all entries for a search have arrived, + the entries are moved from secondary to primary cache, and a time + stamp is given to the entries. */ +static int +memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes) +{ + ldapmemcacheReqId reqid; + + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + reqid.ldmemcrid_msgid = msgid; + reqid.ldmemcrid_ld = ld; + + return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND_LAST, + (void*)&reqid, (void*)pRes, NULL); +} + +/* Removes entries from the temporary cache. */ +static int +memcache_remove(LDAP *ld, int msgid) +{ + ldapmemcacheReqId reqid; + + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + reqid.ldmemcrid_msgid = msgid; + reqid.ldmemcrid_ld = ld; + + return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE, + (void*)&reqid, NULL, NULL); +} + +#if 0 /* this function is not used */ +/* Wipes out everything in the temporary cache directory. */ +static int +memcache_remove_all(LDAP *ld) +{ + if (!memcache_exist(ld)) + return LDAP_LOCAL_ERROR; + + return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE_ALL, + NULL, NULL, NULL); +} +#endif /* 0 */ + +/* Returns TRUE or FALSE */ +static int +memcache_exist(LDAP *ld) +{ + return (ld->ld_memcache != NULL); +} + +/* Attaches cached entries to an ldap handle. */ +static int +memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg) +{ + int nRes = LDAP_SUCCESS; + LDAPMessage **r; + LDAPMessage *pCopy; + + nRes = memcache_dup_message(pMsg, msgid, 1, &pCopy, NULL); + if (nRes != LDAP_SUCCESS) + return nRes; + + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + + for (r = &(ld->ld_responses); *r; r = &((*r)->lm_next)) + if ((*r)->lm_msgid == msgid) + break; + + if (*r) + for (r = &((*r)->lm_chain); *r; r = &((*r)->lm_chain)) { + ; + } + + *r = pCopy; + + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + + return nRes; +} + +/* Check if main_dn is included in {dn, scope} */ +static int +memcache_compare_dn(const char *main_dn, const char *dn, int scope) +{ + int nRes; + char **components = NULL; + char **main_components = NULL; + + components = ldap_explode_dn(dn, 0); + main_components = ldap_explode_dn(main_dn, 0); + + if (!components || !main_components) { + nRes = LDAP_COMPARE_TRUE; + } + else { + + int i, main_i; + + main_i = ldap_count_values(main_components) - 1; + i = ldap_count_values(components) - 1; + + for (; i >= 0 && main_i >= 0; i--, main_i--) { + if (strcasecmp(main_components[main_i], components[i])) + break; + } + + if (i >= 0 && main_i >= 0) { + nRes = LDAP_COMPARE_FALSE; + } + else if (i < 0 && main_i < 0) { + if (scope != LDAP_SCOPE_ONELEVEL) + nRes = LDAP_COMPARE_TRUE; + else + nRes = LDAP_COMPARE_FALSE; + } + else if (main_i < 0) { + nRes = LDAP_COMPARE_FALSE; + } + else { + if (scope == LDAP_SCOPE_BASE) + nRes = LDAP_COMPARE_FALSE; + else if (scope == LDAP_SCOPE_SUBTREE) + nRes = LDAP_COMPARE_TRUE; + else if (main_i == 0) + nRes = LDAP_COMPARE_TRUE; + else + nRes = LDAP_COMPARE_FALSE; + } + } + + if (components) + ldap_value_free(components); + + if (main_components) + ldap_value_free(main_components); + + return nRes; +} + +/* Dup a complete separate copy of a berelement, including the buffers + the berelement points to. */ +static BerElement* +memcache_ber_dup(BerElement* pBer, unsigned long *pSize) +{ + BerElement *p = ber_dup(pBer); + + *pSize = 0; + + if (p) { + + *pSize += sizeof(BerElement) + EXTRA_SIZE; + + if (p->ber_len <= EXTRA_SIZE) { + p->ber_flags |= LBER_FLAG_NO_FREE_BUFFER; + p->ber_buf = (char*)p + sizeof(BerElement); + } else { + p->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER; + p->ber_buf = (char*)NSLDAPI_CALLOC(1, p->ber_len); + *pSize += (p->ber_buf ? p->ber_len : 0); + } + + if (p->ber_buf) { + p->ber_ptr = p->ber_buf + (pBer->ber_ptr - pBer->ber_buf); + p->ber_end = p->ber_buf + p->ber_len; + memcpy(p->ber_buf, pBer->ber_buf, p->ber_len); + } else { + ber_free(p, 0); + p = NULL; + *pSize = 0; + } + } + + return p; +} + +/* Dup a entry or a chain of entries. */ +static int +memcache_dup_message(LDAPMessage *res, int msgid, int fromcache, + LDAPMessage **ppResCopy, unsigned long *pSize) +{ + int nRes = LDAP_SUCCESS; + unsigned long ber_size; + LDAPMessage *pCur; + LDAPMessage **ppCurNew; + + *ppResCopy = NULL; + + if (pSize) + *pSize = 0; + + /* Make a copy of res */ + for (pCur = res, ppCurNew = ppResCopy; pCur; + pCur = pCur->lm_chain, ppCurNew = &((*ppCurNew)->lm_chain)) { + + if ((*ppCurNew = (LDAPMessage*)NSLDAPI_CALLOC(1, + sizeof(LDAPMessage))) == NULL) { + nRes = LDAP_NO_MEMORY; + break; + } + + memcpy(*ppCurNew, pCur, sizeof(LDAPMessage)); + (*ppCurNew)->lm_next = NULL; + (*ppCurNew)->lm_ber = memcache_ber_dup(pCur->lm_ber, &ber_size); + (*ppCurNew)->lm_msgid = msgid; + (*ppCurNew)->lm_fromcache = (fromcache != 0); + + if (pSize) + *pSize += sizeof(LDAPMessage) + ber_size; + } + + if ((nRes != LDAP_SUCCESS) && (*ppResCopy != NULL)) { + ldap_msgfree(*ppResCopy); + *ppResCopy = NULL; + if (pSize) + *pSize = 0; + } + + return nRes; +} + +/************************* Cache Functions ***********************/ + +/* Frees a cache header. */ +static int +memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes) +{ + if (pRes) { + + unsigned long size = sizeof(ldapmemcacheRes); + + if (pRes->ldmemcr_basedn) { + size += strlen(pRes->ldmemcr_basedn) + 1; + NSLDAPI_FREE(pRes->ldmemcr_basedn); + } + + if (pRes->ldmemcr_resHead) { + size += pRes->ldmemcr_resSize; + ldap_msgfree(pRes->ldmemcr_resHead); + } + + NSLDAPI_FREE(pRes); + + memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES, + MEMCACHE_SIZE_DEDUCT); + } + + return( LDAP_SUCCESS ); +} + +/* Detaches a cache header from the list of headers. */ +static int +memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index) +{ + if (pRes->ldmemcr_prev[index]) + pRes->ldmemcr_prev[index]->ldmemcr_next[index] = + pRes->ldmemcr_next[index]; + + if (pRes->ldmemcr_next[index]) + pRes->ldmemcr_next[index]->ldmemcr_prev[index] = + pRes->ldmemcr_prev[index]; + + if (cache->ldmemc_resHead[index] == pRes) + cache->ldmemc_resHead[index] = pRes->ldmemcr_next[index]; + + if (cache->ldmemc_resTail[index] == pRes) + cache->ldmemc_resTail[index] = pRes->ldmemcr_prev[index]; + + pRes->ldmemcr_prev[index] = NULL; + pRes->ldmemcr_next[index] = NULL; + + return( LDAP_SUCCESS ); +} + +/* Inserts a new cache header to a list of headers. */ +static int +memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index) +{ + if (cache->ldmemc_resHead[index]) + cache->ldmemc_resHead[index]->ldmemcr_prev[index] = pRes; + else + cache->ldmemc_resTail[index] = pRes; + + pRes->ldmemcr_prev[index] = NULL; + pRes->ldmemcr_next[index] = cache->ldmemc_resHead[index]; + cache->ldmemc_resHead[index] = pRes; + + return( LDAP_SUCCESS ); +} + +/* Appends a chain of entries to the given cache header. */ +static int +memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg, + unsigned long size) +{ + if (pRes->ldmemcr_resTail) + pRes->ldmemcr_resTail->lm_chain = pMsg; + else + pRes->ldmemcr_resHead = pMsg; + + for (pRes->ldmemcr_resTail = pMsg; + pRes->ldmemcr_resTail->lm_chain; + pRes->ldmemcr_resTail = pRes->ldmemcr_resTail->lm_chain) { + ; + } + + pRes->ldmemcr_resSize += size; + + return( LDAP_SUCCESS ); +} + + +#ifdef LDAP_DEBUG +static void +memcache_print_list( LDAPMemCache *cache, int index ) +{ + char *name; + ldapmemcacheRes *restmp; + + switch( index ) { + case LIST_TTL: + name = "TTL"; + break; + case LIST_LRU: + name = "LRU"; + break; + case LIST_TMP: + name = "TMP"; + break; + case LIST_TOTAL: + name = "TOTAL"; + break; + default: + name = "unknown"; + } + + LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%p %s list:\n", + cache, name, 0 ); + for ( restmp = cache->ldmemc_resHead[index]; restmp != NULL; + restmp = restmp->ldmemcr_next[index] ) { + LDAPDebug( LDAP_DEBUG_TRACE, + " key: 0x%8.8lx, ld: 0x%p, msgid: %d\n", + restmp->ldmemcr_crc_key, + restmp->ldmemcr_req_id.ldmemcrid_ld, + restmp->ldmemcr_req_id.ldmemcrid_msgid ); + } + LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%p end of %s list.\n", + cache, name, 0 ); +} +#endif /* LDAP_DEBUG */ + +/* Tells whether a cached result has expired. */ +static int +memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes, + unsigned long curTime) +{ + if (!cache->ldmemc_ttl) + return 0; + + return ((unsigned long)difftime( + (time_t)curTime, + (time_t)(pRes->ldmemcr_timestamp)) >= + cache->ldmemc_ttl); +} + +/* Operates the cache in a central place. */ +static int +memcache_access(LDAPMemCache *cache, int mode, + void *pData1, void *pData2, void *pData3) +{ + int nRes = LDAP_SUCCESS; + unsigned long size = 0; + + /* Add a new cache header to the cache. */ + if (mode == MEMCACHE_ACCESS_ADD) { + unsigned long key = *((unsigned long*)pData1); + char *basedn = (char*)pData3; + ldapmemcacheRes *pRes = NULL; + void* hashResult = NULL; + + nRes = htable_get(cache->ldmemc_resTmp, pData2, &hashResult); + if (nRes == LDAP_SUCCESS) + return( LDAP_ALREADY_EXISTS ); + + pRes = (ldapmemcacheRes*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheRes)); + if (pRes == NULL) + return( LDAP_NO_MEMORY ); + + pRes->ldmemcr_crc_key = key; + pRes->ldmemcr_req_id = *((ldapmemcacheReqId*)pData2); + pRes->ldmemcr_basedn = (basedn ? nsldapi_strdup(basedn) : NULL); + + size += sizeof(ldapmemcacheRes) + strlen(basedn) + 1; + nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES, + MEMCACHE_SIZE_ADD); + if (nRes == LDAP_SUCCESS) + nRes = htable_put(cache->ldmemc_resTmp, pData2, (void*)pRes); + if (nRes == LDAP_SUCCESS) + memcache_add_to_list(cache, pRes, LIST_TMP); + else + memcache_free_entry(cache, pRes); + } + /* Append entries to an existing cache header. */ + else if ((mode == MEMCACHE_ACCESS_APPEND) || + (mode == MEMCACHE_ACCESS_APPEND_LAST)) { + + LDAPMessage *pMsg = (LDAPMessage*)pData2; + LDAPMessage *pCopy = NULL; + ldapmemcacheRes *pRes = NULL; + void* hashResult = NULL; + + nRes = htable_get(cache->ldmemc_resTmp, pData1, &hashResult); + if (nRes != LDAP_SUCCESS) + return nRes; + + pRes = (ldapmemcacheRes*) hashResult; + nRes = memcache_dup_message(pMsg, pMsg->lm_msgid, 0, &pCopy, &size); + if (nRes != LDAP_SUCCESS) { + nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TMP); + memcache_free_entry(cache, pRes); + return nRes; + } + + nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES, + MEMCACHE_SIZE_ADD); + if (nRes != LDAP_SUCCESS) { + ldap_msgfree(pCopy); + nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TMP); + memcache_free_entry(cache, pRes); + return nRes; + } + + memcache_add_res_to_list(pRes, pCopy, size); + + if (mode == MEMCACHE_ACCESS_APPEND) + return( LDAP_SUCCESS ); + + nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TMP); + (pRes->ldmemcr_req_id).ldmemcrid_ld = NULL; + (pRes->ldmemcr_req_id).ldmemcrid_msgid = -1; + pRes->ldmemcr_timestamp = (unsigned long)time(NULL); + + if ((nRes = htable_put(cache->ldmemc_resLookup, + (void*)&(pRes->ldmemcr_crc_key), + (void*)pRes)) == LDAP_SUCCESS) { + memcache_add_to_list(cache, pRes, LIST_TTL); + memcache_add_to_list(cache, pRes, LIST_LRU); + } else { + memcache_free_entry(cache, pRes); + } + } + /* Search for cached entries for a particular search. */ + else if (mode == MEMCACHE_ACCESS_FIND) { + + ldapmemcacheRes **ppRes = (ldapmemcacheRes**)pData2; + + nRes = htable_get(cache->ldmemc_resLookup, pData1, (void**)ppRes); + if (nRes != LDAP_SUCCESS) + return nRes; + + if (!memcache_expired(cache, *ppRes, (unsigned long)time(0))) { + memcache_free_from_list(cache, *ppRes, LIST_LRU); + memcache_add_to_list(cache, *ppRes, LIST_LRU); + return( LDAP_SUCCESS ); + } + + nRes = htable_remove(cache->ldmemc_resLookup, pData1, NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, *ppRes, LIST_TTL); + memcache_free_from_list(cache, *ppRes, LIST_LRU); + memcache_free_entry(cache, *ppRes); + nRes = LDAP_NO_SUCH_OBJECT; + *ppRes = NULL; + } + /* Remove cached entries in the temporary cache. */ + else if (mode == MEMCACHE_ACCESS_DELETE) { + + void* hashResult = NULL; + + if ((nRes = htable_remove(cache->ldmemc_resTmp, pData1, + &hashResult)) == LDAP_SUCCESS) { + ldapmemcacheRes *pCurRes = (ldapmemcacheRes*) hashResult; + memcache_free_from_list(cache, pCurRes, LIST_TMP); + memcache_free_entry(cache, pCurRes); + } + } + /* Wipe out the temporary cache. */ + else if (mode == MEMCACHE_ACCESS_DELETE_ALL) { + + nRes = htable_removeall(cache->ldmemc_resTmp, (void*)cache); + } + /* Remove expired entries from primary cache. */ + else if (mode == MEMCACHE_ACCESS_UPDATE) { + + ldapmemcacheRes *pCurRes = cache->ldmemc_resTail[LIST_TTL]; + unsigned long curTime = (unsigned long)time(NULL); + + for (; pCurRes; pCurRes = cache->ldmemc_resTail[LIST_TTL]) { + + if (!memcache_expired(cache, pCurRes, curTime)) + break; + + nRes = htable_remove(cache->ldmemc_resLookup, + (void*)&(pCurRes->ldmemcr_crc_key), NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pCurRes, LIST_TTL); + memcache_free_from_list(cache, pCurRes, LIST_LRU); + memcache_free_entry(cache, pCurRes); + } + } + /* Wipe out the primary cache. */ + else if (mode == MEMCACHE_ACCESS_FLUSH_ALL) { + + ldapmemcacheRes *pCurRes = cache->ldmemc_resHead[LIST_TTL]; + + nRes = htable_removeall(cache->ldmemc_resLookup, (void*)cache); + + for (; pCurRes; pCurRes = cache->ldmemc_resHead[LIST_TTL]) { + memcache_free_from_list(cache, pCurRes, LIST_LRU); + cache->ldmemc_resHead[LIST_TTL] = + cache->ldmemc_resHead[LIST_TTL]->ldmemcr_next[LIST_TTL]; + memcache_free_entry(cache, pCurRes); + } + cache->ldmemc_resTail[LIST_TTL] = NULL; + } + /* Remove cached entries in both primary and temporary cache. */ + else if ((mode == MEMCACHE_ACCESS_FLUSH) || + (mode == MEMCACHE_ACCESS_FLUSH_RESULTS)) { + + int i, list_id, bDone; + int scope = (int)pData2; + char *dn = (char*)pData1; + char *dnTmp; + BerElement ber; + LDAPMessage *pMsg; + ldapmemcacheRes *pRes; + + if (cache->ldmemc_basedns) { + for (i = 0; cache->ldmemc_basedns[i]; i++) { + if ((memcache_compare_dn(cache->ldmemc_basedns[i], dn, + LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) || + (memcache_compare_dn(dn, cache->ldmemc_basedns[i], + LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE)) + break; + } + if (cache->ldmemc_basedns[i] == NULL) + return( LDAP_SUCCESS ); + } + + for (i = 0; i < 2; i++) { + + list_id = (i == 0 ? LIST_TTL : LIST_TMP); + + for (pRes = cache->ldmemc_resHead[list_id]; pRes != NULL; + pRes = pRes->ldmemcr_next[list_id]) { + + int foundentries = 0; + + if ((memcache_compare_dn(pRes->ldmemcr_basedn, dn, + LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE) && + (memcache_compare_dn(dn, pRes->ldmemcr_basedn, + LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE)) + continue; + + for (pMsg = pRes->ldmemcr_resHead, bDone = 0; + !bDone && pMsg; pMsg = pMsg->lm_chain) { + + if (!NSLDAPI_IS_SEARCH_ENTRY( pMsg->lm_msgtype )) + continue; + foundentries = 1; + ber = *(pMsg->lm_ber); + if (ber_scanf(&ber, "{a", &dnTmp) != LBER_ERROR) { + bDone = (memcache_compare_dn(dnTmp, dn, scope) == + LDAP_COMPARE_TRUE); + ldap_memfree(dnTmp); + } + } + + /* If we're in the result flush mode, and the base matched, and + there were no entries in the result, we'll flush this cache + slot, as opposed to the MEMCACHE_ACCESS_FLUSH mode which does + not flush negative results. */ + if ((mode == MEMCACHE_ACCESS_FLUSH_RESULTS) && !foundentries) { + bDone = 1; + } + + if (!bDone) + continue; + + if (list_id == LIST_TTL) { + nRes = htable_remove(cache->ldmemc_resLookup, + (void*)&(pRes->ldmemcr_crc_key), NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TTL); + memcache_free_from_list(cache, pRes, LIST_LRU); + } else { + nRes = htable_remove(cache->ldmemc_resTmp, + (void*)&(pRes->ldmemcr_req_id), NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TMP); + } + memcache_free_entry(cache, pRes); + } + } + } + /* Flush least recently used entries from cache */ + else if (mode == MEMCACHE_ACCESS_FLUSH_LRU) { + + ldapmemcacheRes *pRes = cache->ldmemc_resTail[LIST_LRU]; + + if (pRes == NULL) + return LDAP_NO_SUCH_OBJECT; + + LDAPDebug( LDAP_DEBUG_TRACE, + "memcache_access FLUSH_LRU: removing key 0x%8.8lx\n", + pRes->ldmemcr_crc_key, 0, 0 ); + nRes = htable_remove(cache->ldmemc_resLookup, + (void*)&(pRes->ldmemcr_crc_key), NULL); + assert(nRes == LDAP_SUCCESS); + memcache_free_from_list(cache, pRes, LIST_TTL); + memcache_free_from_list(cache, pRes, LIST_LRU); + memcache_free_entry(cache, pRes); + } + /* Unknown command */ + else { + nRes = LDAP_PARAM_ERROR; + } + + return nRes; +} + +static void +memcache_flush( LDAPMemCache *cache, char *dn, int scope, int flushresults ) +{ + if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) { + return; + } + + LDAP_MEMCACHE_MUTEX_LOCK( cache ); + + if (!dn) { + memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL); + } else { + if (flushresults) { + memcache_access(cache, MEMCACHE_ACCESS_FLUSH_RESULTS, + (void*)dn, (void*)scope, NULL); + } else { + memcache_access(cache, MEMCACHE_ACCESS_FLUSH, + (void*)dn, (void*)scope, NULL); + } + } + + LDAP_MEMCACHE_MUTEX_UNLOCK( cache ); +} + + +#ifdef LDAP_DEBUG +static void +memcache_report_statistics( LDAPMemCache *cache ) +{ + unsigned long hitrate; + + if ( cache->ldmemc_stats.ldmemcstat_tries == 0 ) { + hitrate = 0; + } else { + hitrate = ( 100L * cache->ldmemc_stats.ldmemcstat_hits ) / + cache->ldmemc_stats.ldmemcstat_tries; + } + LDAPDebug( LDAP_DEBUG_STATS, "memcache 0x%p:\n", cache, 0, 0 ); + LDAPDebug( LDAP_DEBUG_STATS, " tries: %ld hits: %ld hitrate: %ld%%\n", + cache->ldmemc_stats.ldmemcstat_tries, + cache->ldmemc_stats.ldmemcstat_hits, hitrate ); + if ( cache->ldmemc_size <= 0 ) { /* no size limit */ + LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld\n", + cache->ldmemc_size_used, 0, 0 ); + } else { + LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld free: %ld\n", + cache->ldmemc_size_used, + cache->ldmemc_size - cache->ldmemc_size_used, 0 ); + } +} +#endif /* LDAP_DEBUG */ + +/************************ Hash Table Functions *****************************/ + +/* Calculates size (# of entries) of hash table given the size limit for + the cache. */ +static int +htable_calculate_size(int sizelimit) +{ + int i, j; + int size = (int)(((double)sizelimit / + (double)(sizeof(BerElement) + EXTRA_SIZE)) / 1.5); + + /* Get a prime # */ + size = (size & 0x1 ? size : size + 1); + for (i = 3, j = size / 2; i < j; i++) { + if ((size % i) == 0) { + size += 2; + i = 3; + j = size / 2; + } + } + + return size; +} + +/* Returns the size in bytes of the given hash table. */ +static int +htable_sizeinbytes(HashTable *pTable) +{ + if (!pTable) + return 0; + + return (pTable->size * sizeof(HashTableNode)); +} + +/* Inserts an item into the hash table. */ +static int +htable_put(HashTable *pTable, void *key, void *pData) +{ + int index = pTable->hashfunc(pTable->size, key); + + if (index >= 0 && index < pTable->size) + return pTable->putdata(&(pTable->table[index].pData), key, pData); + + return( LDAP_OPERATIONS_ERROR ); +} + +/* Retrieves an item from the hash table. */ +static int +htable_get(HashTable *pTable, void *key, void **ppData) +{ + int index = pTable->hashfunc(pTable->size, key); + + *ppData = NULL; + + if (index >= 0 && index < pTable->size) + return pTable->getdata(pTable->table[index].pData, key, ppData); + + return( LDAP_OPERATIONS_ERROR ); +} + +/* Performs a miscellaneous operation on a hash table entry. */ +static int +htable_misc(HashTable *pTable, void *key, void *pData) +{ + if (pTable->miscfunc) { + int index = pTable->hashfunc(pTable->size, key); + if (index >= 0 && index < pTable->size) + return pTable->miscfunc(&(pTable->table[index].pData), key, pData); + } + + return( LDAP_OPERATIONS_ERROR ); +} + +/* Removes an item from the hash table. */ +static int +htable_remove(HashTable *pTable, void *key, void **ppData) +{ + int index = pTable->hashfunc(pTable->size, key); + + if (ppData) + *ppData = NULL; + + if (index >= 0 && index < pTable->size) + return pTable->removedata(&(pTable->table[index].pData), key, ppData); + + return( LDAP_OPERATIONS_ERROR ); +} + +/* Removes everything in the hash table. */ +static int +htable_removeall(HashTable *pTable, void *pData) +{ + int i; + + for (i = 0; i < pTable->size; i++) + pTable->clrtablenode(&(pTable->table[i].pData), pData); + + return( LDAP_SUCCESS ); +} + +/* Creates a new hash table. */ +static int +htable_create(int size_limit, HashFuncPtr hashf, + PutDataPtr putDataf, GetDataPtr getDataf, + RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef, + MiscFuncPtr miscOpf, HashTable **ppTable) +{ + size_limit = htable_calculate_size(size_limit); + + if ((*ppTable = (HashTable*)NSLDAPI_CALLOC(1, sizeof(HashTable))) == NULL) + return( LDAP_NO_MEMORY ); + + (*ppTable)->table = (HashTableNode*)NSLDAPI_CALLOC(size_limit, + sizeof(HashTableNode)); + if ((*ppTable)->table == NULL) { + NSLDAPI_FREE(*ppTable); + *ppTable = NULL; + return( LDAP_NO_MEMORY ); + } + + (*ppTable)->size = size_limit; + (*ppTable)->hashfunc = hashf; + (*ppTable)->putdata = putDataf; + (*ppTable)->getdata = getDataf; + (*ppTable)->miscfunc = miscOpf; + (*ppTable)->removedata = removeDataf; + (*ppTable)->clrtablenode = clrNodef; + + return( LDAP_SUCCESS ); +} + +/* Destroys a hash table. */ +static int +htable_free(HashTable *pTable) +{ + NSLDAPI_FREE(pTable->table); + NSLDAPI_FREE(pTable); + return( LDAP_SUCCESS ); +} + +/**************** Hash table callbacks for temporary cache ****************/ + +/* Hash function */ +static int +msgid_hashf(int table_size, void *key) +{ + unsigned code = (unsigned)((ldapmemcacheReqId*)key)->ldmemcrid_ld; + return (((code << 20) + (code >> 12)) % table_size); +} + +/* Called by hash table to insert an item. */ +static int +msgid_putdata(void **ppTableData, void *key, void *pData) +{ + ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key; + ldapmemcacheRes *pRes = (ldapmemcacheRes*)pData; + ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData; + ldapmemcacheRes *pCurRes = *ppHead; + ldapmemcacheRes *pPrev = NULL; + + for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) { + if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld) + break; + pPrev = pCurRes; + } + + if (pCurRes) { + for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) { + if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid == + pReqId->ldmemcrid_msgid) + return( LDAP_ALREADY_EXISTS ); + pPrev = pCurRes; + } + pPrev->ldmemcr_next[LIST_TTL] = pRes; + pRes->ldmemcr_prev[LIST_TTL] = pPrev; + pRes->ldmemcr_next[LIST_TTL] = NULL; + } else { + if (pPrev) + pPrev->ldmemcr_htable_next = pRes; + else + *ppHead = pRes; + pRes->ldmemcr_htable_next = NULL; + } + + return( LDAP_SUCCESS ); +} + +/* Called by hash table to retrieve an item. */ +static int +msgid_getdata(void *pTableData, void *key, void **ppData) +{ + ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key; + ldapmemcacheRes *pCurRes = (ldapmemcacheRes*)pTableData; + + *ppData = NULL; + + for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) { + if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld) + break; + } + + if (!pCurRes) + return( LDAP_NO_SUCH_OBJECT ); + + for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) { + if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid == + pReqId->ldmemcrid_msgid) { + *ppData = (void*)pCurRes; + return( LDAP_SUCCESS ); + } + } + + return( LDAP_NO_SUCH_OBJECT ); +} + +/* Called by hash table to remove an item. */ +static int +msgid_removedata(void **ppTableData, void *key, void **ppData) +{ + ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData); + ldapmemcacheRes *pCurRes = NULL; + ldapmemcacheRes *pPrev = NULL; + ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key; + + if (ppData) + *ppData = NULL; + + for (; pHead; pHead = pHead->ldmemcr_htable_next) { + if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld) + break; + pPrev = pHead; + } + + if (!pHead) + return( LDAP_NO_SUCH_OBJECT ); + + for (pCurRes = pHead; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) { + if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid == + pReqId->ldmemcrid_msgid) + break; + } + + if (!pCurRes) + return( LDAP_NO_SUCH_OBJECT ); + + if (ppData) { + pCurRes->ldmemcr_next[LIST_TTL] = NULL; + pCurRes->ldmemcr_prev[LIST_TTL] = NULL; + pCurRes->ldmemcr_htable_next = NULL; + *ppData = (void*)pCurRes; + } + + if (pCurRes != pHead) { + if (pCurRes->ldmemcr_prev[LIST_TTL]) + pCurRes->ldmemcr_prev[LIST_TTL]->ldmemcr_next[LIST_TTL] = + pCurRes->ldmemcr_next[LIST_TTL]; + if (pCurRes->ldmemcr_next[LIST_TTL]) + pCurRes->ldmemcr_next[LIST_TTL]->ldmemcr_prev[LIST_TTL] = + pCurRes->ldmemcr_prev[LIST_TTL]; + return( LDAP_SUCCESS ); + } + + if (pPrev) { + if (pHead->ldmemcr_next[LIST_TTL]) { + pPrev->ldmemcr_htable_next = pHead->ldmemcr_next[LIST_TTL]; + pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next = + pHead->ldmemcr_htable_next; + } else { + pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next; + } + } else { + if (pHead->ldmemcr_next[LIST_TTL]) { + *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_next[LIST_TTL]; + pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next = + pHead->ldmemcr_htable_next; + } else { + *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next; + } + } + + return( LDAP_SUCCESS ); +} + +/* Called by hash table to remove all cached entries associated to searches + being performed using the given ldap handle. */ +static int +msgid_clear_ld_items(void **ppTableData, void *key, void *pData) +{ + LDAPMemCache *cache = (LDAPMemCache*)pData; + ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData); + ldapmemcacheRes *pPrev = NULL; + ldapmemcacheRes *pCurRes = NULL; + ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key; + + for (; pHead; pHead = pHead->ldmemcr_htable_next) { + if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld) + break; + pPrev = pHead; + } + + if (!pHead) + return( LDAP_NO_SUCH_OBJECT ); + + if (pPrev) + pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next; + else + *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next; + + for (pCurRes = pHead; pHead; pCurRes = pHead) { + pHead = pHead->ldmemcr_next[LIST_TTL]; + memcache_free_from_list(cache, pCurRes, LIST_TMP); + memcache_free_entry(cache, pCurRes); + } + + return( LDAP_SUCCESS ); +} + +/* Called by hash table for removing all items in the table. */ +static void +msgid_clearnode(void **ppTableData, void *pData) +{ + LDAPMemCache *cache = (LDAPMemCache*)pData; + ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData; + ldapmemcacheRes *pSubHead = *ppHead; + ldapmemcacheRes *pCurRes = NULL; + + for (; *ppHead; pSubHead = *ppHead) { + ppHead = &((*ppHead)->ldmemcr_htable_next); + for (pCurRes = pSubHead; pSubHead; pCurRes = pSubHead) { + pSubHead = pSubHead->ldmemcr_next[LIST_TTL]; + memcache_free_from_list(cache, pCurRes, LIST_TMP); + memcache_free_entry(cache, pCurRes); + } + } +} + +/********************* Hash table for primary cache ************************/ + +/* Hash function */ +static int +attrkey_hashf(int table_size, void *key) +{ + return ((*((unsigned long*)key)) % table_size); +} + +/* Called by hash table to insert an item. */ +static int +attrkey_putdata(void **ppTableData, void *key, void *pData) +{ + unsigned long attrkey = *((unsigned long*)key); + ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData; + ldapmemcacheRes *pRes = *ppHead; + + for (; pRes; pRes = pRes->ldmemcr_htable_next) { + if (pRes->ldmemcr_crc_key == attrkey) + return( LDAP_ALREADY_EXISTS ); + } + + pRes = (ldapmemcacheRes*)pData; + pRes->ldmemcr_htable_next = *ppHead; + *ppHead = pRes; + + return( LDAP_SUCCESS ); +} + +/* Called by hash table to retrieve an item. */ +static int +attrkey_getdata(void *pTableData, void *key, void **ppData) +{ + unsigned long attrkey = *((unsigned long*)key); + ldapmemcacheRes *pRes = (ldapmemcacheRes*)pTableData; + + for (; pRes; pRes = pRes->ldmemcr_htable_next) { + if (pRes->ldmemcr_crc_key == attrkey) { + *ppData = (void*)pRes; + return( LDAP_SUCCESS ); + } + } + + *ppData = NULL; + + return( LDAP_NO_SUCH_OBJECT ); +} + +/* Called by hash table to remove an item. */ +static int +attrkey_removedata(void **ppTableData, void *key, void **ppData) +{ + unsigned long attrkey = *((unsigned long*)key); + ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData; + ldapmemcacheRes *pRes = *ppHead; + ldapmemcacheRes *pPrev = NULL; + + for (; pRes; pRes = pRes->ldmemcr_htable_next) { + if (pRes->ldmemcr_crc_key == attrkey) { + if (ppData) + *ppData = (void*)pRes; + if (pPrev) + pPrev->ldmemcr_htable_next = pRes->ldmemcr_htable_next; + else + *ppHead = pRes->ldmemcr_htable_next; + pRes->ldmemcr_htable_next = NULL; + return( LDAP_SUCCESS ); + } + pPrev = pRes; + } + + if (ppData) + *ppData = NULL; + + return( LDAP_NO_SUCH_OBJECT ); +} + +/* Called by hash table for removing all items in the table. */ +static void +attrkey_clearnode(void **ppTableData, void *pData) +{ + ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData; + ldapmemcacheRes *pRes = *ppHead; + + (void)pData; + + for (; *ppHead; pRes = *ppHead) { + ppHead = &((*ppHead)->ldmemcr_htable_next); + pRes->ldmemcr_htable_next = NULL; + } +} + +/***************************** CRC algorithm ********************************/ + +/* From http://www.faqs.org/faqs/compression-faq/part1/section-25.html */ + +/* + * Build auxiliary table for parallel byte-at-a-time CRC-32. + */ +#define NSLDAPI_CRC32_POLY 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ + +static nsldapi_uint_32 crc32_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; + +/* Initialized first time "crc32()" is called. If you prefer, you can + * statically initialize it at compile time. [Another exercise.] + */ + +static unsigned long +crc32_convert(char *buf, int len) +{ + unsigned char *p; + nsldapi_uint_32 crc; + + crc = 0xffffffff; /* preload shift register, per CRC-32 spec */ + for (p = (unsigned char *)buf; len > 0; ++p, --len) + crc = ((crc << 8) ^ crc32_table[(crc >> 24) ^ *p]) & 0xffffffff; + + return (unsigned long) ~crc; /* transmit complement, per CRC-32 spec */ +} diff --git a/ldap/c-sdk/libraries/libldap/message.c b/ldap/c-sdk/libraries/libldap/message.c new file mode 100644 index 0000000000..bfbd200f87 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/message.c @@ -0,0 +1,105 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +int +LDAP_CALL +ldap_msgid( LDAPMessage *lm ) +{ + if ( !NSLDAPI_VALID_LDAPMESSAGE_POINTER( lm )) { + return( -1 ); + } + + return( lm->lm_msgid ); +} + +int +LDAP_CALL +ldap_msgtype( LDAPMessage *lm ) +{ + if ( !NSLDAPI_VALID_LDAPMESSAGE_POINTER( lm )) { + return( -1 ); + } + + return( lm->lm_msgtype ); +} + + +LDAPMessage * +LDAP_CALL +ldap_first_message( LDAP *ld, LDAPMessage *chain ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULLMSG ); /* punt */ + } + + return( chain ); +} + + +LDAPMessage * +LDAP_CALL +ldap_next_message( LDAP *ld, LDAPMessage *msg ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( NULLMSG ); /* punt */ + } + + if ( msg == NULLMSG || msg->lm_chain == NULLMSG ) { + return( NULLMSG ); + } + + return( msg->lm_chain ); +} + + +int +LDAP_CALL +ldap_count_messages( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); + } + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + i++; + } + + return( i ); +} diff --git a/ldap/c-sdk/libraries/libldap/modify.c b/ldap/c-sdk/libraries/libldap/modify.c new file mode 100644 index 0000000000..ea81102c4c --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/modify.c @@ -0,0 +1,226 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * modify.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_modify - initiate an ldap modify operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * mods List of modifications to make. This is null-terminated + * array of struct ldapmod's, specifying the modifications + * to perform. + * + * Example: + * LDAPMod *mods[] = { + * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_REPLACE, "sn", { "jensen", 0 } }, + * 0 + * } + * msgid = ldap_modify( ld, dn, mods ); + */ +int +LDAP_CALL +ldap_modify( LDAP *ld, const char *dn, LDAPMod **mods ) +{ + int msgid; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_modify\n", 0, 0, 0 ); + + if ( ldap_modify_ext( ld, dn, mods, NULL, NULL, &msgid ) + == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + +int +LDAP_CALL +ldap_modify_ext( LDAP *ld, const char *dn, LDAPMod **mods, + LDAPControl **serverctrls, LDAPControl **clientctrls, int *msgidp ) +{ + BerElement *ber; + int i, rc, lderr; + + /* + * A modify request looks like this: + * ModifyRequet ::= SEQUENCE { + * object DistinguishedName, + * modifications SEQUENCE OF SEQUENCE { + * operation ENUMERATED { + * add (0), + * delete (1), + * replace (2) + * }, + * modification SEQUENCE { + * type AttributeType, + * values SET OF AttributeValue + * } + * } + * } + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_modify_ext\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + if ( !NSLDAPI_VALID_LDAPMESSAGE_POINTER( msgidp )) + { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( !NSLDAPI_VALID_NONEMPTY_LDAPMOD_ARRAY( mods )) { + lderr = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + return( lderr ); + } + if ( dn == NULL ) { + dn = ""; + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* see if we should add to the cache */ + if ( ld->ld_cache_on && ld->ld_cache_modify != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_modify)( ld, *msgidp, LDAP_REQ_MODIFY, + dn, mods )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* create a message to send */ + if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( lderr ); + } + + if ( ber_printf( ber, "{it{s{", *msgidp, LDAP_REQ_MODIFY, dn ) + == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + /* for each modification to be performed... */ + for ( i = 0; mods[i] != NULL; i++ ) { + if (( mods[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + rc = ber_printf( ber, "{e{s[V]}}", + mods[i]->mod_op & ~LDAP_MOD_BVALUES, + mods[i]->mod_type, mods[i]->mod_bvalues ); + } else { + rc = ber_printf( ber, "{e{s[v]}}", mods[i]->mod_op, + mods[i]->mod_type, mods[i]->mod_values ); + } + + if ( rc == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + } + + if ( ber_printf( ber, "}}" ) == -1 ) { + lderr = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + ber_free( ber, 1 ); + return( lderr ); + } + + if (( lderr = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( lderr ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_MODIFY, + (char *)dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +int +LDAP_CALL +ldap_modify_s( LDAP *ld, const char *dn, LDAPMod **mods ) +{ + return( ldap_modify_ext_s( ld, dn, mods, NULL, NULL )); +} + +int +LDAP_CALL +ldap_modify_ext_s( LDAP *ld, const char *dn, LDAPMod **mods, + LDAPControl **serverctrls, LDAPControl **clientctrls ) +{ + int msgid, err; + LDAPMessage *res; + + if (( err = ldap_modify_ext( ld, dn, mods, serverctrls, clientctrls, + &msgid )) != LDAP_SUCCESS ) { + return( err ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, &res ) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/moz.build b/ldap/c-sdk/libraries/libldap/moz.build new file mode 100644 index 0000000000..05c7a6b524 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/moz.build @@ -0,0 +1,88 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include('/ldap/ldap-sdk.mozbuild') + +SharedLibrary('ldap60') + +SOURCES += [ + 'abandon.c', + 'add.c', + 'authzidctrl.c', + 'bind.c', + 'cache.c', + 'charray.c', + 'charset.c', + 'compare.c', + 'compat.c', + 'control.c', + 'countvalues.c', + 'delete.c', + 'disptmpl.c', + 'dsparse.c', + 'error.c', + 'extendop.c', + 'free.c', + 'freevalues.c', + 'friendly.c', + 'getattr.c', + 'getdn.c', + 'getdxbyname.c', + 'geteffectiverightsctrl.c', + 'getentry.c', + 'getfilter.c', + 'getoption.c', + 'getvalues.c', + 'memcache.c', + 'message.c', + 'modify.c', + 'open.c', + 'os-ip.c', + 'proxyauthctrl.c', + 'psearch.c', + 'pwmodext.c', + 'pwpctrl.c', + 'referral.c', + 'regex.c', + 'rename.c', + 'request.c', + 'reslist.c', + 'result.c', + 'saslbind.c', + 'sbind.c', + 'search.c', + 'setoption.c', + 'sort.c', + 'sortctrl.c', + 'srchpref.c', + 'tmplout.c', + 'ufn.c', + 'unbind.c', + 'unescape.c', + 'url.c', + 'userstatusctrl.c', + 'utf8.c', + 'vlistctrl.c', + 'whoami.c', +] + +if CONFIG['OS_TARGET'] == 'WINNT': + SOURCES += [ + 'dllmain.c', + 'mozock.c', + ] + DEFFILE = SRCDIR + '/libldap.def' + +if CONFIG['OS_TARGET'] != 'WINNT': + DEFINES['USE_WAITPID'] = True + DEFINES['USE_PTHREADS'] = True + +DEFINES['NEEDPROTOS'] = True + +LOCAL_INCLUDES += [ + '/ldap/c-sdk/include' +] + +USE_LIBS += ['lber60'] diff --git a/ldap/c-sdk/libraries/libldap/mozock.c b/ldap/c-sdk/libraries/libldap/mozock.c new file mode 100644 index 0000000000..4f0bc57501 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/mozock.c @@ -0,0 +1,714 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef _WINDOWS +#define FD_SETSIZE 30000 +#endif + +#include +#include +#include + +// Purpose of this file is to implement an intermediate layer to our network +// services, the winsock. +// This intermediate layer will be able to function with and without a working +// winsock being present. +// The attempt to activate the winsock happens as would normally be expected, +// through the calling application's entry point to us, WSAStartup. + + +// Name of the winsock we would like to load. +// Diffs between OSs, Win32s is out in the cold if running 32 bits unless +// they also have a winsock name wsock32.dll. +#ifndef _WIN32 +#define SZWINSOCK "winsock.dll" +#else +#define SZWINSOCK "wsock32.dll" +#endif + +// Here is the enumeration for the winsock functions we have currently +// overridden (needed to run). Add more when needed. +// We use these to access proc addresses, and to hold a table of strings +// to obtain the proc addresses. +enum SockProc { + sp_WSAAsyncGetHostByName = 0, + sp_WSAAsyncSelect, + sp_WSACleanup, + sp_WSAGetLastError, + sp_WSASetLastError, + sp_WSAStartup, + sp___WSAFDIsSet, + sp_accept, + sp_bind, + sp_closesocket, + sp_connect, + sp_gethostbyname, + sp_gethostbyaddr, + sp_gethostname, + sp_getpeername, + sp_getsockname, + sp_getsockopt, + sp_getprotobyname, + sp_htonl, + sp_htons, + sp_inet_addr, + sp_ioctlsocket, + sp_listen, + sp_ntohl, + sp_ntohs, + sp_recv, + sp_select, + sp_send, + sp_setsockopt, + sp_shutdown, + sp_socket, + sp_inet_ntoa, + + sp_MaxProcs // Total count. +}; + +// Array of function names used in GetProcAddress to fill in our +// proc array when needed. +// This array must match the enumerations exactly. +char *spName[(int)sp_MaxProcs] = { + "WSAAsyncGetHostByName", + "WSAAsyncSelect", + "WSACleanup", + "WSAGetLastError", + "WSASetLastError", + "WSAStartup", + "__WSAFDIsSet", + "accept", + "bind", + "closesocket", + "connect", + "gethostbyname", + "gethostbyaddr", + "gethostname", + "getpeername", + "getsockname", + "getsockopt", + "getprotobyname", + "htonl", + "htons", + "inet_addr", + "ioctlsocket", + "listen", + "ntohl", + "ntohs", + "recv", + "select", + "send", + "setsockopt", + "shutdown", + "socket", + "inet_ntoa" +}; + +// Array of proc addresses to the winsock functions. +// These can be NULL, indicating their absence (as in the case we couldn't +// load the winsock.dll or one of the functions wasn't loaded). +// The procs assigned in must corellate with the enumerations exactly. +FARPROC spArray[(int)sp_MaxProcs]; + +// Typedef all the different types of functions that we must cast the +// procs to in order to call without the compiler barfing. +// Prefix is always sp. +// Retval is next, spelled out. +// Parameters in their order are next, spelled out. +typedef int (PASCAL FAR *sp_int_WORD_LPWSADATA)(WORD, LPWSADATA); +typedef int (PASCAL FAR *sp_int_void)(void); +typedef HANDLE (PASCAL FAR *sp_HANDLE_HWND_uint_ccharFARp_charFARp_int)(HWND, unsigned int, const char FAR *, char FAR *, int); +typedef int (PASCAL FAR *sp_int_SOCKET_HWND_uint_long)(SOCKET, HWND, unsigned int, long); +typedef void (PASCAL FAR *sp_void_int)(int); +typedef int (PASCAL FAR *sp_int_SOCKET_fdsetFARp)(SOCKET, fd_set FAR *); +typedef SOCKET(PASCAL FAR *sp_SOCKET_SOCKET_sockaddrFARp_intFARp)(SOCKET, struct sockaddr FAR *, int FAR *); +typedef int (PASCAL FAR *sp_int_SOCKET_csockaddrFARp_int)(SOCKET, const struct sockaddr FAR *, int); +typedef int (PASCAL FAR *sp_int_SOCKET)(SOCKET); +typedef struct hostent FAR *(PASCAL FAR *sp_hostentFARp_ccharFARp)(const char FAR *); +typedef struct hostent FAR *(PASCAL FAR *sp_hostentFARp_ccharFARp_int_int)(const char FAR *, int, int); +typedef int (PASCAL FAR *sp_int_charFARp_int)(char FAR *, int); +typedef int (PASCAL FAR *sp_int_SOCKET_sockaddrFARp_intFARp)(SOCKET, struct sockaddr FAR *, int FAR *); +typedef int (PASCAL FAR *sp_int_SOCKET_int_int_charFARp_intFARp)(SOCKET, int, int, char FAR *, int FAR *); +typedef u_long (PASCAL FAR *sp_ulong_ulong)(u_long); +typedef u_short (PASCAL FAR *sp_ushort_ushort)(u_short); +typedef unsigned long (PASCAL FAR *sp_ulong_ccharFARp)(const char FAR *); +typedef int (PASCAL FAR *sp_int_SOCKET_long_ulongFARp)(SOCKET, long, u_long FAR *); +typedef int (PASCAL FAR *sp_int_SOCKET_int)(SOCKET, int); +typedef int (PASCAL FAR *sp_int_SOCKET_charFARp_int_int)(SOCKET, char FAR *, int, int); +typedef int (PASCAL FAR *sp_int_int_fdsetFARp_fdsetFARp_fdsetFARp_ctimevalFARp)(int,fd_set FAR *,fd_set FAR *,fd_set FAR *,const struct timeval FAR*); +typedef int (PASCAL FAR *sp_int_SOCKET_ccharFARp_int_int)(SOCKET, const char FAR *, int, int); +typedef int (PASCAL FAR *sp_int_SOCKET_int_int_ccharFARp_int)(SOCKET, int, int, const char FAR *, int); +typedef SOCKET (PASCAL FAR *sp_SOCKET_int_int_int)(int, int, int); +typedef char FAR * (PASCAL FAR *sp_charFARp_in_addr)(struct in_addr in); +typedef struct protoent FAR * (PASCAL FAR *sp_protoentFARcchar)(const char FAR *); + +// Handle to the winsock, if loaded. +HINSTANCE hWinsock = NULL; + +#ifndef _WIN32 +// Last error code for the winsock. +int ispError = 0; +#endif + + +BOOL IsWinsockLoaded (int sp) +{ + if (hWinsock == NULL) + { + WSADATA wsaData; +#ifdef _WIN32 + static LONG sc_init = 0; + static DWORD sc_done = 0; + static CRITICAL_SECTION sc; +#endif + /* We need to wait here because another thread might be + in the routine already */ +#ifdef _WIN32 + if (0 == InterlockedExchange(&sc_init,1)) { + InitializeCriticalSection(&sc); + sc_done = 1; + } + while (0 == sc_done) Sleep(0); + EnterCriticalSection(&sc); + if (hWinsock == NULL) { +#endif + WSAStartup(0x0101, &wsaData); +#ifdef _WIN32 + } + LeaveCriticalSection(&sc); +#endif + } +// Quick macro to tell if the winsock has actually loaded for a particular +// function. +// Debug version is a little more strict to make sure you get the names right. +#ifdef DEBUG + return hWinsock != NULL && spArray[(int)(sp)] != NULL; +#else // A little faster + return hWinsock != NULL; +#endif +} + +// Here are the functions that we have taken over by not directly linking +// with the winsock import library or importing through the def file. + +/* In win16 we simulate blocking commands as follows. Prior to issuing the + * command we make the socket not-blocking (WSAAsyncSelect does that). + * We then issue the command and see if it would have blocked. If so, we + * yield the processor and go to sleep until an event occurs that unblocks + * us (WSAAsyncSelect allowed us to register what that condition is). We + * keep repeating until we do not get a would-block indication when issuing + * the command. At that time we unregister the notification condition and + * return the result of the command to the caller. + */ + +//#ifndef _WIN32 +#if 0 +#define NON_BLOCKING(command,condition,index,type) \ + type iret; \ + HWND hWndFrame = AfxGetApp()->m_pMainWnd->m_hWnd; \ + while (TRUE) { \ + if (WSAAsyncSelect(s, hWndFrame, msg_NetActivity, condition) \ + == SOCKET_ERROR) { \ + break; \ + } \ + if(IsWinsockLoaded(index)) { \ + iret=command; \ + if (!(iret==SOCKET_ERROR && WSAGetLastError()==WSAEWOULDBLOCK)) { \ + WSAAsyncSelect(s, hWndFrame, msg_NetActivity, 0); \ + return iret; \ + } \ + PR_Yield(); \ + } else { \ + break; \ + } \ + } +#else +#define NON_BLOCKING(command,condition,index,type) \ + if(IsWinsockLoaded(index)) { \ + return command; \ + } +#endif + +int PASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData) { + // Our default return value is failure, though we change this regardless. + int iRetval = WSAVERNOTSUPPORTED; + HINSTANCE MyHandle; + + // Before doing anything, clear out our proc array. + memset(spArray, 0, sizeof(spArray)); + + // attempt to load the real winsock. + MyHandle = LoadLibrary(SZWINSOCK); +#ifdef _WIN32 + if(MyHandle != NULL) { +#else + if(MyHandle > HINSTANCE_ERROR) { +#endif + // Winsock was loaded. + // Get the proc addresses for each needed function next. + int spTraverse; + for(spTraverse = 0; spTraverse < (int)sp_MaxProcs; spTraverse++) { + spArray[spTraverse] = GetProcAddress(MyHandle, spName[spTraverse]); + if ( NULL == spArray[spTraverse] ) + return iRetval;// Bad winsock? Bad function name? + } + + hWinsock = MyHandle; + // AllRight, attempt to make our first proxied call. + if(IsWinsockLoaded(sp_WSAStartup)) { + iRetval = ((sp_int_WORD_LPWSADATA)spArray[sp_WSAStartup])(wVersionRequested, lpWSAData); + } + + // If the return value is still an error at this point, we unload the DLL, + // so that we can act as though nothing happened and the user + // gets no network access. + if(iRetval != 0) { + // Clear out our proc array. + memset(spArray, 0, sizeof(spArray)); + + // Free up the winsock. + FreeLibrary(MyHandle); + MyHandle = NULL; + } + } +#ifndef _WIN32 + else { + // Failed to load. + // Set this to NULL so it is clear. + hWinsock = NULL; + } +#endif + + + // Check our return value, if it isn't success, then we need to fake + // our own winsock implementation. + if(iRetval != 0) { + // We always return success. + iRetval = 0; + + // Fill in the structure. + // Return the version requested as the version supported. + lpWSAData->wVersion = wVersionRequested; + lpWSAData->wHighVersion = wVersionRequested; + + // Fill in a discription. + strcpy(lpWSAData->szDescription, "Mozock DLL internal implementation."); + strcpy(lpWSAData->szSystemStatus, "Winsock running, allowing no network access."); + + // Report a nice round number for sockets and datagram sizes. + lpWSAData->iMaxSockets = 4096; + lpWSAData->iMaxUdpDg = 4096; + + // No vendor information. + lpWSAData->lpVendorInfo = NULL; + } + + return(iRetval); +} + +int PASCAL FAR WSACleanup(void) { + int iRetval = 0; + + // Handling normally or internally. + // When IsWinsockLoaded() is called and hWinsock is NULL, it winds up calling WSAStartup + // which wedges rpcrt4.dll on win95 with some winsock implementations. Bug: 81359. + if(hWinsock && IsWinsockLoaded(sp_WSACleanup)) { + // Call their cleanup routine. + // We could set the return value here, but it is meaning less. + // We always return success. + iRetval = ((sp_int_void)spArray[sp_WSACleanup])(); + //ASSERT(iRetval == 0); + iRetval = 0; + } + + // Wether or not it succeeded, we free off the library here. + // Clear out our proc table too. + memset(spArray, 0, sizeof(spArray)); + if(hWinsock != NULL) { + FreeLibrary(hWinsock); + hWinsock = NULL; + } + + return(iRetval); +} + +HANDLE PASCAL FAR WSAAsyncGetHostByName(HWND hWnd, unsigned int wMsg, const char FAR *name, char FAR *buf, int buflen) { + // Normal or shim. + if(IsWinsockLoaded(sp_WSAAsyncGetHostByName)) { + return(((sp_HANDLE_HWND_uint_ccharFARp_charFARp_int)spArray[sp_WSAAsyncGetHostByName])(hWnd, wMsg, name, buf, buflen)); + } + + // Must return error here. + // Set our last error value to be that the net is down. + WSASetLastError(WSAENETDOWN); + return(NULL); +} + +int PASCAL FAR WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent) { + // Normal or shim. + if(IsWinsockLoaded(sp_WSAAsyncSelect)) { + return(((sp_int_SOCKET_HWND_uint_long)spArray[sp_WSAAsyncSelect])(s, hWnd, wMsg, lEvent)); + } + + // Must return error here. + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR WSAGetLastError(void) { + // See if someone else can handle. + if(IsWinsockLoaded(sp_WSAGetLastError)) { + return(((sp_int_void)spArray[sp_WSAGetLastError])()); + } + +#ifndef _WIN32 + { + // Fake it. + int iRetval = ispError; + ispError = 0; + return(iRetval); + } +#else + // Use default OS handler. + return(GetLastError()); +#endif +} + +void PASCAL FAR WSASetLastError(int iError) { + // See if someone else can handle. + if(IsWinsockLoaded(sp_WSASetLastError)) { + ((sp_void_int)spArray[sp_WSASetLastError])(iError); + return; + } + +#ifndef _WIN32 + // Fake it. + ispError = iError; + return; +#else + // Use default OS handler. + SetLastError(iError); + return; +#endif +} + +int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *set) { + int i; + + // See if someone else will handle. + if(IsWinsockLoaded(sp___WSAFDIsSet)) { + return(((sp_int_SOCKET_fdsetFARp)spArray[sp___WSAFDIsSet])(fd, set)); + } + + // Default implementation. + i = set->fd_count; + while (i--) { + if (set->fd_array[i] == fd) { + return 1; + } + } + return 0; +} + +SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen) { + // Internally or shim + NON_BLOCKING( + (((sp_SOCKET_SOCKET_sockaddrFARp_intFARp)spArray[sp_accept])(s, addr, addrlen)), + FD_ACCEPT, sp_accept, SOCKET); + + // Fail. + WSASetLastError(WSAENETDOWN); + return(INVALID_SOCKET); +} + +int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR *name, int namelen) { + // Internally or shim + if(IsWinsockLoaded(sp_bind)) { + return(((sp_int_SOCKET_csockaddrFARp_int)spArray[sp_bind])(s, name, namelen)); + } + + // Fail. + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR closesocket(SOCKET s) { + // Internally or shim. + NON_BLOCKING( + (((sp_int_SOCKET)spArray[sp_closesocket])(s)), + FD_CLOSE, sp_closesocket, int); + + // Error. + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR *name, int namelen) { + // Internally or shim. + if(IsWinsockLoaded(sp_connect)) { + /* This could block and so it would seem that the NON_BLOCK + * macro should be used here. However it was causing a crash + * and so it was decided to allow blocking here instead + */ + return (((sp_int_SOCKET_csockaddrFARp_int)spArray[sp_connect])(s, name, namelen)); + } + + // Err. + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +struct hostent FAR * PASCAL FAR gethostbyname(const char FAR *name) { + if(IsWinsockLoaded(sp_gethostbyname)) { + return(((sp_hostentFARp_ccharFARp)spArray[sp_gethostbyname])(name)); + } + + WSASetLastError(WSAENETDOWN); + return(NULL); +} + +struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR *addr, int len, int type) { + if(IsWinsockLoaded(sp_gethostbyaddr)) { + return(((sp_hostentFARp_ccharFARp_int_int)spArray[sp_gethostbyaddr])(addr, len, type)); + } + + WSASetLastError(WSAENETDOWN); + return(NULL); +} + +int PASCAL FAR gethostname(char FAR *name, int namelen) { + if(IsWinsockLoaded(sp_gethostname)) { + return(((sp_int_charFARp_int)spArray[sp_gethostname])(name, namelen)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR getpeername(SOCKET s, struct sockaddr FAR *name, int FAR *namelen) { + if(IsWinsockLoaded(sp_getpeername)) { + return(((sp_int_SOCKET_sockaddrFARp_intFARp)spArray[sp_getpeername])(s, name, namelen)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR getsockname(SOCKET s, struct sockaddr FAR *name, int FAR *namelen) { + if(IsWinsockLoaded(sp_getsockname)) { + return(((sp_int_SOCKET_sockaddrFARp_intFARp)spArray[sp_getsockname])(s, name, namelen)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR getsockopt(SOCKET s, int level, int optname, char FAR *optval, int FAR *optlen) { + if(IsWinsockLoaded(sp_getsockopt)) { + return(((sp_int_SOCKET_int_int_charFARp_intFARp)spArray[sp_getsockopt])(s, level, optname, optval, optlen)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +struct protoent FAR * PASCAL getprotobyname(const char FAR * name) { + if(IsWinsockLoaded(sp_getprotobyname)) { + return(((sp_protoentFARcchar)spArray[sp_getprotobyname])(name)); + } + + WSASetLastError(WSAENETDOWN); + return NULL; +} + +u_long PASCAL FAR htonl(u_long hostlong) { + if(IsWinsockLoaded(sp_htonl)) { + return(((sp_ulong_ulong)spArray[sp_htonl])(hostlong)); + } + +#ifndef _WIN32 + return + (((hostlong&0xff)<<24) + ((hostlong&0xff00)<<8) + + ((hostlong&0xff0000)>>8) + ((hostlong&0xff000000)>>24)); + +#else + // Just return what was passed in. + return(hostlong); +#endif +} + +u_short PASCAL FAR htons(u_short hostshort) { + if(IsWinsockLoaded(sp_htons)) { + return(((sp_ushort_ushort)spArray[sp_htons])(hostshort)); + } + +#ifndef _WIN32 + return (((hostshort&0xff)<<8) + ((hostshort&0xff00)>>8)); + +#else + // Just return what was passed in. + return(hostshort); +#endif +} + +u_long PASCAL FAR ntohl(u_long hostlong) { + if(IsWinsockLoaded(sp_ntohl)) { + return(((sp_ulong_ulong)spArray[sp_ntohl])(hostlong)); + } + +#ifndef _WIN32 + return + (((hostlong&0xff)<<24) + ((hostlong&0xff00)<<8) + + ((hostlong&0xff0000)>>8) + ((hostlong&0xff000000)>>24)); + +#else + // Just return what was passed in. + return(hostlong); +#endif +} + +u_short PASCAL FAR ntohs(u_short hostshort) { + if(IsWinsockLoaded(sp_ntohs)) { + return(((sp_ushort_ushort)spArray[sp_ntohs])(hostshort)); + } + +#ifndef _WIN32 + return (((hostshort&0xff)<<8) + ((hostshort&0xff00)>>8)); + +#else + // Just return what was passed in. + return(hostshort); +#endif +} + +unsigned long PASCAL FAR inet_addr(const char FAR *cp) { + if(IsWinsockLoaded(sp_inet_addr)) { + return(((sp_ulong_ccharFARp)spArray[sp_inet_addr])(cp)); + } + + return(INADDR_NONE); +} + +int PASCAL FAR ioctlsocket(SOCKET s, long cmd, u_long FAR *argp) { + if(IsWinsockLoaded(sp_ioctlsocket)) { + return(((sp_int_SOCKET_long_ulongFARp)spArray[sp_ioctlsocket])(s, cmd, argp)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR listen(SOCKET s, int backlog) { + if(IsWinsockLoaded(sp_listen)) { + return(((sp_int_SOCKET_int)spArray[sp_listen])(s, backlog)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags) { + NON_BLOCKING( + (((sp_int_SOCKET_charFARp_int_int)spArray[sp_recv])(s, buf, len, flags)), + FD_READ, sp_recv, int); + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR select(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout) { + // If there's nothing to do, stop now before we go off into dll land. + // Optimization, boyz. + if((readfds && readfds->fd_count) || (writefds && writefds->fd_count) || (exceptfds && exceptfds->fd_count)) { + if(IsWinsockLoaded(sp_select)) { + return(((sp_int_int_fdsetFARp_fdsetFARp_fdsetFARp_ctimevalFARp)spArray[sp_select])(nfds,readfds,writefds,exceptfds,timeout)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); + } + + // No need to go to the DLL, there is nothing to do. + return(0); +} + +int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags) { + NON_BLOCKING( + + (((sp_int_SOCKET_ccharFARp_int_int)spArray[sp_send])(s, buf, len, flags)), + FD_WRITE, sp_send, int); + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR setsockopt(SOCKET s, int level, int optname, const char FAR *optval, int optlen) { + if(IsWinsockLoaded(sp_setsockopt)) { + return(((sp_int_SOCKET_int_int_ccharFARp_int)spArray[sp_setsockopt])(s, level, optname, optval, optlen)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +int PASCAL FAR shutdown(SOCKET s, int how) { + if(IsWinsockLoaded(sp_shutdown)) { + return(((sp_int_SOCKET_int)spArray[sp_shutdown])(s, how)); + } + + WSASetLastError(WSAENETDOWN); + return(SOCKET_ERROR); +} + +SOCKET PASCAL FAR socket(int af, int type, int protocol) { + if(IsWinsockLoaded(sp_socket)) { + return(((sp_SOCKET_int_int_int)spArray[sp_socket])(af, type, protocol)); + } + + WSASetLastError(WSAENETDOWN); + return(INVALID_SOCKET); +} + +char FAR * PASCAL FAR inet_ntoa(struct in_addr in) { + if(IsWinsockLoaded(sp_inet_ntoa)) { + return ((sp_charFARp_in_addr)spArray[sp_inet_ntoa])(in); + } + + WSASetLastError(WSAENETDOWN); + return NULL; +} diff --git a/ldap/c-sdk/libraries/libldap/nsprthreadtest.c b/ldap/c-sdk/libraries/libldap/nsprthreadtest.c new file mode 100644 index 0000000000..f9af68b423 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/nsprthreadtest.c @@ -0,0 +1,621 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include +#include +#include + +#define NAME "cn=Directory Manager" +#define PASSWORD "secret99" +#define BASE "dc=example,dc=com" + +static int simplebind( LDAP *ld, char *msg, int tries ); +static void search_thread( void * ); +static void modify_thread( void * ); +static void add_thread( void * ); +static void delete_thread( void * ); +static void set_ld_error(); +static int get_ld_error(); +static void set_errno(); +static int get_errno(); +static void tsd_setup(); +static void *my_mutex_alloc( void ); +static void my_mutex_free( void * ); +static int my_mutex_lock( void * ); +static int my_mutex_unlock( void * ); +static LDAPHostEnt *my_gethostbyname( const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ); +static LDAPHostEnt *my_gethostbyaddr( const char *addr, int length, + int type, LDAPHostEnt *result, char *buffer, int buflen, + int *statusp, void *extradata ); +static LDAPHostEnt *copyPRHostEnt2LDAPHostEnt( LDAPHostEnt *ldhp, + PRHostEnt *prhp ); + +typedef struct ldapmsgwrapper { + LDAPMessage *lmw_messagep; + struct ldapmsgwrapper *lmw_next; +} ldapmsgwrapper; + + +#define CONNECTION_ERROR( lderr ) ( (lderr) == LDAP_SERVER_DOWN || \ + (lderr) == LDAP_CONNECT_ERROR ) + + +LDAP *ld; +PRUintn tsdindex; +#ifdef LDAP_MEMCACHE +LDAPMemCache *memcache = NULL; +#define MEMCACHE_SIZE (256*1024) /* 256K bytes */ +#define MEMCACHE_TTL (15*60) /* 15 minutes */ +#endif + + +main( int argc, char **argv ) +{ + PRThread *search_tid, *search_tid2, *search_tid3; + PRThread *search_tid4, *modify_tid, *add_tid; + PRThread *delete_tid; + struct ldap_thread_fns tfns; + struct ldap_dns_fns dnsfns; + int rc; + + if ( argc != 3 ) { + fprintf( stderr, "usage: %s host port\n", argv[0] ); + exit( 1 ); + } + + PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 0 ); + if ( PR_NewThreadPrivateIndex( &tsdindex, NULL ) != PR_SUCCESS ) { + perror( "PR_NewThreadPrivateIndex" ); + exit( 1 ); + } + tsd_setup(); /* for main thread */ + + if ( (ld = ldap_init( argv[1], atoi( argv[2] ) )) == NULL ) { + perror( "ldap_open" ); + exit( 1 ); + } + + /* set thread function pointers */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_mutex_alloc = my_mutex_alloc; + tfns.ltf_mutex_free = my_mutex_free; + tfns.ltf_mutex_lock = my_mutex_lock; + tfns.ltf_mutex_unlock = my_mutex_unlock; + tfns.ltf_get_errno = get_errno; + tfns.ltf_set_errno = set_errno; + tfns.ltf_get_lderrno = get_ld_error; + tfns.ltf_set_lderrno = set_ld_error; + tfns.ltf_lderrno_arg = NULL; + if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *) &tfns ) + != 0 ) { + ldap_perror( ld, "ldap_set_option: thread functions" ); + exit( 1 ); + } + + /* set DNS function pointers */ + memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) ); + dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE; + dnsfns.lddnsfn_gethostbyname = my_gethostbyname; + dnsfns.lddnsfn_gethostbyaddr = my_gethostbyaddr; + if ( ldap_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns ) + != 0 ) { + ldap_perror( ld, "ldap_set_option: DNS functions" ); + exit( 1 ); + } + +#ifdef LDAP_MEMCACHE + /* create the in-memory cache */ + if (( rc = ldap_memcache_init( MEMCACHE_TTL, MEMCACHE_SIZE, NULL, + &tfns, &memcache )) != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_memcache_init failed - %s\n", + ldap_err2string( rc )); + exit( 1 ); + } + if (( rc = ldap_memcache_set( ld, memcache )) != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_memcache_set failed - %s\n", + ldap_err2string( rc )); + exit( 1 ); + } +#endif + + /* + * set option so that the next call to ldap_simple_bind_s() after + * the server connection is lost will attempt to reconnect. + */ + if ( ldap_set_option( ld, LDAP_OPT_RECONNECT, LDAP_OPT_ON ) != 0 ) { + ldap_perror( ld, "ldap_set_option: reconnect" ); + exit( 1 ); + } + + /* initial bind */ + if ( simplebind( ld, "ldap_simple_bind_s/main", 1 ) != LDAP_SUCCESS ) { + exit( 1 ); + } + + /* create the operation threads */ + if ( (search_tid = PR_CreateThread( PR_USER_THREAD, search_thread, + "1", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread search_thread" ); + exit( 1 ); + } + if ( (modify_tid = PR_CreateThread( PR_USER_THREAD, modify_thread, + "2", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread modify_thread" ); + exit( 1 ); + } + if ( (search_tid2 = PR_CreateThread( PR_USER_THREAD, search_thread, + "3", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread search_thread 2" ); + exit( 1 ); + } + if ( (add_tid = PR_CreateThread( PR_USER_THREAD, add_thread, + "4", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread add_thread" ); + exit( 1 ); + } + if ( (search_tid3 = PR_CreateThread( PR_USER_THREAD, search_thread, + "5", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread search_thread 3" ); + exit( 1 ); + } + if ( (delete_tid = PR_CreateThread( PR_USER_THREAD, delete_thread, + "6", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread delete_thread" ); + exit( 1 ); + } + if ( (search_tid4 = PR_CreateThread( PR_USER_THREAD, search_thread, + "7", PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, + 0 )) == NULL ) { + perror( "PR_CreateThread search_thread 4" ); + exit( 1 ); + } + + PR_Cleanup(); + return( 0 ); +} + + +static int +simplebind( LDAP *ld, char *msg, int tries ) +{ + int rc; + + while ( tries-- > 0 ) { + rc = ldap_simple_bind_s( ld, NAME, PASSWORD ); + if ( rc != LDAP_SUCCESS ) { + ldap_perror( ld, msg ); + } + if ( tries == 0 || !CONNECTION_ERROR( rc )) { + return( rc ); + } + fprintf( stderr, + "%s: sleeping for 5 secs - will try %d more time(s)...\n", + msg, tries ); + sleep( 5 ); + } + + return( rc ); +} + + +static void +search_thread( void *arg1 ) +{ + LDAPMessage *res; + LDAPMessage *e; + char *a; + char **v; + char *dn; + BerElement *ber; + int i, rc, msgid; + void *tsd; + char *id = arg1; + + printf( "search_thread\n" ); + tsd_setup(); + for ( ;; ) { + printf( "%sSearching...\n", id ); + if ( (msgid = ldap_search( ld, BASE, LDAP_SCOPE_SUBTREE, + "(objectclass=*)", NULL, 0 )) == -1 ) { + ldap_perror( ld, "ldap_search_s" ); + rc = ldap_get_lderrno( ld, NULL, NULL ); + if ( CONNECTION_ERROR( rc ) && simplebind( ld, + "bind-search_thread", 5 ) != LDAP_SUCCESS ) { + return; + } + continue; + } + while ( (rc = ldap_result( ld, msgid, 0, NULL, &res )) + == LDAP_RES_SEARCH_ENTRY ) { + for ( e = ldap_first_entry( ld, res ); e != NULL; + e = ldap_next_entry( ld, e ) ) { + dn = ldap_get_dn( ld, e ); + /* printf( "%sdn: %s\n", id, dn ); */ + free( dn ); + for ( a = ldap_first_attribute( ld, e, &ber ); + a != NULL; a = ldap_next_attribute( ld, e, + ber ) ) { + v = ldap_get_values( ld, e, a ); + for ( i = 0; v && v[i] != 0; i++ ) { + /* + printf( "%s%s: %s\n", id, a, + v[i] ); + */ + } + ldap_value_free( v ); + ldap_memfree( a ); + } + if ( ber != NULL ) { + ber_free( ber, 0 ); + } + } + ldap_msgfree( res ); + /* printf( "%s\n", id ); */ + } + + if ( rc == -1 || ldap_result2error( ld, res, 0 ) != + LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_search" ); + } else { + printf( "%sDone with one round\n", id ); + } + + if ( rc == -1 ) { + rc = ldap_get_lderrno( ld, NULL, NULL ); + if ( CONNECTION_ERROR( rc ) && simplebind( ld, + "bind-search_thread", 5 ) != LDAP_SUCCESS ) { + return; + } + } + } +} + +static void +modify_thread( void *arg1 ) +{ + LDAPMessage *res; + LDAPMessage *e; + int i, modentry, entries, msgid, rc; + LDAPMod mod; + LDAPMod *mods[2]; + char *vals[2]; + char *dn; + char *id = arg1; + ldapmsgwrapper *list, *lmwp, *lastlmwp; + + printf( "modify_thread\n" ); + tsd_setup(); + if ( (msgid = ldap_search( ld, BASE, LDAP_SCOPE_SUBTREE, + "(objectclass=*)", NULL, 0 )) == -1 ) { + ldap_perror( ld, "ldap_search_s" ); + exit( 1 ); + } + entries = 0; + list = lastlmwp = NULL; + while ( (rc = ldap_result( ld, msgid, 0, NULL, &res )) + == LDAP_RES_SEARCH_ENTRY ) { + entries++; + if (( lmwp = (ldapmsgwrapper *) + malloc( sizeof( ldapmsgwrapper ))) == NULL ) { + perror( "modify_thread: malloc" ); + exit( 1 ); + } + lmwp->lmw_messagep = res; + lmwp->lmw_next = NULL; + if ( lastlmwp == NULL ) { + list = lastlmwp = lmwp; + } else { + lastlmwp->lmw_next = lmwp; + } + lastlmwp = lmwp; + } + if ( rc == -1 || ldap_result2error( ld, res, 0 ) != LDAP_SUCCESS ) { + ldap_perror( ld, "modify_thread: ldap_search" ); + exit( 1 ); + } else { + entries++; + printf( "%sModify got %d entries\n", id, entries ); + } + + mods[0] = &mod; + mods[1] = NULL; + vals[0] = "bar"; + vals[1] = NULL; + for ( ;; ) { + modentry = rand() % entries; + for ( i = 0, lmwp = list; lmwp != NULL && i < modentry; + i++, lmwp = lmwp->lmw_next ) { + /* NULL */ + } + + if ( lmwp == NULL ) { + fprintf( stderr, + "%sModify could not find entry %d of %d\n", + id, modentry, entries ); + continue; + } + e = lmwp->lmw_messagep; + printf( "%sPicked entry %d of %d\n", id, i, entries ); + dn = ldap_get_dn( ld, e ); + mod.mod_op = LDAP_MOD_REPLACE; + mod.mod_type = "description"; + mod.mod_values = vals; + printf( "%sModifying (%s)\n", id, dn ); + if (( rc = ldap_modify_s( ld, dn, mods )) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_modify_s" ); + if ( CONNECTION_ERROR( rc ) && simplebind( ld, + "bind-modify_thread", 5 ) != LDAP_SUCCESS ) { + return; + } + } + free( dn ); + } +} + +static void +add_thread( void *arg1 ) +{ + LDAPMod mod[5]; + LDAPMod *mods[6]; + char dn[BUFSIZ], name[40]; + char *cnvals[2], *snvals[2], *ocvals[2]; + int i, rc; + char *id = arg1; + + printf( "add_thread\n" ); + tsd_setup(); + for ( i = 0; i < 5; i++ ) { + mods[i] = &mod[i]; + } + mods[5] = NULL; + mod[0].mod_op = 0; + mod[0].mod_type = "cn"; + mod[0].mod_values = cnvals; + cnvals[1] = NULL; + mod[1].mod_op = 0; + mod[1].mod_type = "sn"; + mod[1].mod_values = snvals; + snvals[1] = NULL; + mod[2].mod_op = 0; + mod[2].mod_type = "objectclass"; + mod[2].mod_values = ocvals; + ocvals[0] = "person"; + ocvals[1] = NULL; + mods[3] = NULL; + + for ( ;; ) { + sprintf( name, "%d", rand() ); + sprintf( dn, "cn=%s, " BASE, name ); + cnvals[0] = name; + snvals[0] = name; + + printf( "%sAdding entry (%s)\n", id, dn ); + if (( rc = ldap_add_s( ld, dn, mods )) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_add_s" ); + if ( CONNECTION_ERROR( rc ) && simplebind( ld, + "bind-add_thread", 5 ) != LDAP_SUCCESS ) { + return; + } + } + } +} + +static void +delete_thread( void *arg1 ) +{ + LDAPMessage *res; + char dn[BUFSIZ], name[40]; + int entries, msgid, rc; + char *id = arg1; + + printf( "delete_thread\n" ); + tsd_setup(); + if ( (msgid = ldap_search( ld, BASE, LDAP_SCOPE_SUBTREE, + "(objectclass=*)", NULL, 0 )) == -1 ) { + ldap_perror( ld, "delete_thread: ldap_search_s" ); + exit( 1 ); + } + entries = 0; + while ( (rc = ldap_result( ld, msgid, 0, NULL, &res )) + == LDAP_RES_SEARCH_ENTRY ) { + entries++; + ldap_msgfree( res ); + } + entries++; + if ( rc == -1 || ldap_result2error( ld, res, 1 ) != LDAP_SUCCESS ) { + ldap_perror( ld, "delete_thread: ldap_search" ); + } else { + printf( "%sDelete got %d entries\n", id, entries ); + } + + for ( ;; ) { + sprintf( name, "%d", rand() ); + sprintf( dn, "cn=%s, " BASE, name ); + + printf( "%sDeleting entry (%s)\n", id, dn ); + if (( rc = ldap_delete_s( ld, dn )) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_delete_s" ); + if ( CONNECTION_ERROR( rc ) && simplebind( ld, + "bind-delete_thread", 5 ) != LDAP_SUCCESS ) { + return; + } + } + } +} + +struct ldap_error { + int le_errno; + char *le_matched; + char *le_errmsg; +}; + +static void +tsd_setup() +{ + void *tsd; + + tsd = (void *) PR_GetThreadPrivate( tsdindex ); + if ( tsd != NULL ) { + fprintf( stderr, "tsd non-null!\n" ); + exit( 1 ); + } + tsd = (void *) calloc( 1, sizeof(struct ldap_error) ); + if ( PR_SetThreadPrivate( tsdindex, tsd ) != 0 ) { + perror( "PR_SetThreadPrivate" ); + exit( 1 ); + } +} + +static void +set_ld_error( int err, char *matched, char *errmsg, void *dummy ) +{ + struct ldap_error *le; + + le = (void *) PR_GetThreadPrivate( tsdindex ); + le->le_errno = err; + if ( le->le_matched != NULL ) { + ldap_memfree( le->le_matched ); + } + le->le_matched = matched; + if ( le->le_errmsg != NULL ) { + ldap_memfree( le->le_errmsg ); + } + le->le_errmsg = errmsg; +} + +static int +get_ld_error( char **matchedp, char **errmsgp, void *dummy ) +{ + struct ldap_error *le; + + le = PR_GetThreadPrivate( tsdindex ); + if ( matchedp != NULL ) { + *matchedp = le->le_matched; + } + if ( errmsgp != NULL ) { + *errmsgp = le->le_errmsg; + } + return( le->le_errno ); +} + +static void +set_errno( int oserrno ) +{ + /* XXXmcs: should this be PR_SetError( oserrno, 0 )? */ + PR_SetError( PR_UNKNOWN_ERROR, oserrno ); +} + +static int +get_errno( void ) +{ + /* XXXmcs: should this be PR_GetError()? */ + return( PR_GetOSError()); +} + +static void * +my_mutex_alloc( void ) +{ + return( (void *)PR_NewLock()); +} + +static void +my_mutex_free( void *mutex ) +{ + PR_DestroyLock( (PRLock *)mutex ); +} + +static int +my_mutex_lock( void *mutex ) +{ + PR_Lock( (PRLock *)mutex ); + return( 0 ); +} + +static int +my_mutex_unlock( void *mutex ) +{ + if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) { + return( -1 ); + } + + return( 0 ); +} + +static LDAPHostEnt * +my_gethostbyname( const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ) +{ + PRHostEnt prhent; + + if ( PR_GetHostByName( name, buffer, buflen, + &prhent ) != PR_SUCCESS ) { + return( NULL ); + } + + return( copyPRHostEnt2LDAPHostEnt( result, &prhent )); +} + +static LDAPHostEnt * +my_gethostbyaddr( const char *addr, int length, int type, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ) +{ + PRHostEnt prhent; + + if ( PR_GetHostByAddr( (PRNetAddr *)addr, buffer, buflen, + &prhent ) != PR_SUCCESS ) { + return( NULL ); + } + + return( copyPRHostEnt2LDAPHostEnt( result, &prhent )); +} + +static LDAPHostEnt * +copyPRHostEnt2LDAPHostEnt( LDAPHostEnt *ldhp, PRHostEnt *prhp ) +{ + ldhp->ldaphe_name = prhp->h_name; + ldhp->ldaphe_aliases = prhp->h_aliases; + ldhp->ldaphe_addrtype = prhp->h_addrtype; + ldhp->ldaphe_length = prhp->h_length; + ldhp->ldaphe_addr_list = prhp->h_addr_list; + return( ldhp ); +} diff --git a/ldap/c-sdk/libraries/libldap/open.c b/ldap/c-sdk/libraries/libldap/open.c new file mode 100644 index 0000000000..06cb629cbb --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/open.c @@ -0,0 +1,912 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * open.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" +#ifdef LDAP_SASLIO_HOOKS +/* Valid for any ANSI C compiler */ +#include +extern sasl_callback_t client_callbacks[]; +#endif + +#define VI_PRODUCTVERSION 3 + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK ((unsigned long) 0x7f000001) +#endif + +#ifdef LDAP_DEBUG +int ldap_debug = 0; +#endif + +#ifdef _WINDOWS +#define USE_WINDOWS_TLS /* thread local storage */ +#endif + +/* + * global defaults for callbacks are stored here. callers of the API set + * these by passing a NULL "ld" to ldap_set_option(). Everything in + * nsldapi_ld_defaults can be overridden on a per-ld basis as well (the + * memory allocation functions are global to all ld's). + */ +struct ldap nsldapi_ld_defaults; +struct ldap_memalloc_fns nsldapi_memalloc_fns = { 0, 0, 0, 0 }; +int nsldapi_initialized = 0; + +#ifdef USE_PTHREADS +#include +#ifdef VMS +/* +** pthread_self() is not a routine on OpenVMS; it's inline assembler code. +** Since we need a real address which we can stuff away into a table, we need +** to make sure that pthread_self maps to the real pthread_self routine (yes, +** we do have one fortunately). +*/ +#undef pthread_self +#define pthread_self PTHREAD_SELF +extern pthread_t pthread_self (void); +#endif +static pthread_key_t nsldapi_key; +static pthread_mutex_t nsldapi_init_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct nsldapi_ldap_error { + int le_errno; + char *le_matched; + char *le_errmsg; +}; +#elif defined (USE_WINDOWS_TLS) +static DWORD dwTlsIndex; +struct nsldapi_ldap_error { + int le_errno; + char *le_matched; + char *le_errmsg; +}; +#elif defined (_WINDOWS) /* use static tls */ +__declspec ( thread ) int nsldapi_gldaperrno; +__declspec ( thread ) char *nsldapi_gmatched = NULL; +__declspec ( thread ) char *nsldapi_gldaperror = NULL; +#endif /* USE_WINDOWS_TLS */ + + +#ifdef _WINDOWS +#define LDAP_MUTEX_T HANDLE +static LDAP_MUTEX_T nsldapi_init_mutex; + +int +pthread_mutex_init( LDAP_MUTEX_T *mp, void *attr) +{ + if ( (*mp = CreateMutex(NULL, FALSE, NULL)) == NULL ) + return( 1 ); + else + return( 0 ); +} + +static void * +pthread_mutex_alloc( void ) +{ + LDAP_MUTEX_T *mutexp; + + if ( (mutexp = malloc( sizeof(LDAP_MUTEX_T) )) != NULL ) { + pthread_mutex_init( mutexp, NULL ); + } + return( mutexp ); +} + +int +pthread_mutex_destroy( LDAP_MUTEX_T *mp ) +{ + if ( !(CloseHandle(*mp)) ) + return( 1 ); + else + return( 0 ); +} + +static void +pthread_mutex_free( void *mutexp ) +{ + pthread_mutex_destroy( (LDAP_MUTEX_T *) mutexp ); + free( mutexp ); +} + +int +pthread_mutex_lock( LDAP_MUTEX_T *mp ) +{ + if ( (WaitForSingleObject(*mp, INFINITE) != WAIT_OBJECT_0) ) + return( 1 ); + else + return( 0 ); +} + +int +pthread_mutex_unlock( LDAP_MUTEX_T *mp ) +{ + if ( !(ReleaseMutex(*mp)) ) + return( 1 ); + else + return( 0 ); +} + +static int +get_errno( void ) +{ + return errno; +} + +static void +set_errno( int Errno ) +{ + errno = Errno; +} + +#ifdef USE_WINDOWS_TLS +static void +set_ld_error( int err, char *matched, char *errmsg, void *dummy ) +{ + struct nsldapi_ldap_error *le; + void *tsd; + + le = TlsGetValue( dwTlsIndex ); + + if (le == NULL) { + tsd = (void *)calloc(1, sizeof(struct nsldapi_ldap_error)); + TlsSetValue( dwTlsIndex, tsd ); + } + + le = TlsGetValue ( dwTlsIndex ); + + if (le == NULL) + return; + + le->le_errno = err; + + if ( le->le_matched != NULL ) { + ldap_memfree( le->le_matched ); + } + le->le_matched = matched; + + if ( le->le_errmsg != NULL ) { + ldap_memfree( le->le_errmsg ); + } + le->le_errmsg = errmsg; +} + +static int +get_ld_error ( char **matched, char **errmsg, void *dummy ) +{ + struct nsldapi_ldap_error *le; + + le = TlsGetValue( dwTlsIndex ); + if ( matched != NULL ) { + *matched = le->le_matched; + } + + if ( errmsg != NULL ) { + *errmsg = le->le_errmsg; + } + + return( le->le_errno ); +} +#else +static int +get_ld_error( char **LDMatched, char **LDError, void * Args ) +{ + if ( LDMatched != NULL ) + { + *LDMatched = nsldapi_gmatched; + } + if ( LDError != NULL ) + { + *LDError = nsldapi_gldaperror; + } + return nsldapi_gldaperrno; +} + +static void +set_ld_error( int LDErrno, char * LDMatched, char * LDError, + void * Args ) +{ + /* Clean up any previous string storage. */ + if ( nsldapi_gmatched != NULL ) + { + ldap_memfree( nsldapi_gmatched ); + } + if ( nsldapi_gldaperror != NULL ) + { + ldap_memfree( nsldapi_gldaperror ); + } + + nsldapi_gldaperrno = LDErrno; + nsldapi_gmatched = LDMatched; + nsldapi_gldaperror = LDError; +} +#endif /* USE_WINDOWS_TLS */ +#endif /* ! _WINDOWS */ + +#ifdef USE_PTHREADS +static void * +pthread_mutex_alloc( void ) +{ + pthread_mutex_t *mutexp; + + if ( (mutexp = malloc( sizeof(pthread_mutex_t) )) != NULL ) { + pthread_mutex_init( mutexp, NULL ); + } + return( mutexp ); +} + +static void +pthread_mutex_free( void *mutexp ) +{ + pthread_mutex_destroy( (pthread_mutex_t *) mutexp ); + free( mutexp ); +} + +static void +set_ld_error( int err, char *matched, char *errmsg, void *dummy ) +{ + struct nsldapi_ldap_error *le; + void *tsd; + + le = pthread_getspecific( nsldapi_key ); + + if (le == NULL) { + tsd = (void *)calloc(1, sizeof(struct nsldapi_ldap_error)); + pthread_setspecific( nsldapi_key, tsd ); + } + + le = pthread_getspecific( nsldapi_key ); + + if (le == NULL) + return; + + le->le_errno = err; + + if ( le->le_matched != NULL ) { + ldap_memfree( le->le_matched ); + } + le->le_matched = matched; + + if ( le->le_errmsg != NULL ) { + ldap_memfree( le->le_errmsg ); + } + le->le_errmsg = errmsg; +} + +static int +get_ld_error( char **matched, char **errmsg, void *dummy ) +{ + struct nsldapi_ldap_error *le; + + le = pthread_getspecific( nsldapi_key ); + + if (le == NULL) + return( LDAP_SUCCESS ); + + if ( matched != NULL ) { + *matched = le->le_matched; + } + if ( errmsg != NULL ) { + *errmsg = le->le_errmsg; + } + return( le->le_errno ); +} + +static void +set_errno( int err ) +{ + errno = err; +} + +static int +get_errno( void ) +{ + return( errno ); +} +#endif /* use_pthreads */ + +#if defined(USE_PTHREADS) || defined(_WINDOWS) +static struct ldap_thread_fns + nsldapi_default_thread_fns = { + (void *(*)(void))pthread_mutex_alloc, + (void (*)(void *))pthread_mutex_free, + (int (*)(void *))pthread_mutex_lock, + (int (*)(void *))pthread_mutex_unlock, + (int (*)(void))get_errno, + (void (*)(int))set_errno, + (int (*)(char **, char **, void *))get_ld_error, + (void (*)(int, char *, char *, void *))set_ld_error, + 0 }; + +static struct ldap_extra_thread_fns + nsldapi_default_extra_thread_fns = { + 0, 0, 0, 0, 0, +#ifdef _WINDOWS + 0 +#else + (void *(*)(void))pthread_self +#endif /* _WINDOWS */ + }; +#endif /* use_pthreads || _windows */ + +void +nsldapi_initialize_defaults( void ) +{ +#ifdef _WINDOWS + pthread_mutex_init( &nsldapi_init_mutex, NULL ); +#endif /* _WINDOWS */ + +#if defined(USE_PTHREADS) || defined(_WINDOWS) + pthread_mutex_lock( &nsldapi_init_mutex ); + + if ( nsldapi_initialized ) { + pthread_mutex_unlock( &nsldapi_init_mutex ); + return; + } +#else + if ( nsldapi_initialized ) { + return; + } +#endif /* use_pthreads || _windows */ + +#ifdef USE_PTHREADS + if ( pthread_key_create(&nsldapi_key, free ) != 0) { + perror("pthread_key_create"); + } +#elif defined(USE_WINDOWS_TLS) + dwTlsIndex = TlsAlloc(); +#endif /* USE_WINDOWS_TLS */ + + memset( &nsldapi_memalloc_fns, 0, sizeof( nsldapi_memalloc_fns )); + memset( &nsldapi_ld_defaults, 0, sizeof( nsldapi_ld_defaults )); + nsldapi_ld_defaults.ld_options = LDAP_BITOPT_REFERRALS; + nsldapi_ld_defaults.ld_version = LDAP_VERSION3; + nsldapi_ld_defaults.ld_lberoptions = LBER_OPT_USE_DER; + nsldapi_ld_defaults.ld_refhoplimit = LDAP_DEFAULT_REFHOPLIMIT; + +#ifdef LDAP_SASLIO_HOOKS + /* SASL default option settings */ + nsldapi_ld_defaults.ld_def_sasl_mech = NULL; + nsldapi_ld_defaults.ld_def_sasl_realm = NULL; + nsldapi_ld_defaults.ld_def_sasl_authcid = NULL; + nsldapi_ld_defaults.ld_def_sasl_authzid = NULL; + /* SASL Security properties */ + nsldapi_ld_defaults.ld_sasl_secprops.max_ssf = UINT_MAX; + nsldapi_ld_defaults.ld_sasl_secprops.maxbufsize = SASL_MAX_BUFF_SIZE; + nsldapi_ld_defaults.ld_sasl_secprops.security_flags = + SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS; + + /* SASL mutex function callbacks */ + sasl_set_mutex( + (sasl_mutex_alloc_t *)nsldapi_default_thread_fns.ltf_mutex_alloc, + (sasl_mutex_lock_t *)nsldapi_default_thread_fns.ltf_mutex_lock, + (sasl_mutex_unlock_t *)nsldapi_default_thread_fns.ltf_mutex_unlock, + (sasl_mutex_free_t *)nsldapi_default_thread_fns.ltf_mutex_free ); + + /* SASL memory allocation function callbacks */ + sasl_set_alloc( + (sasl_malloc_t *)ldap_x_malloc, + (sasl_calloc_t *)ldap_x_calloc, + (sasl_realloc_t *)ldap_x_realloc, + (sasl_free_t *)ldap_x_free ); + + /* SASL library initialization */ + if ( sasl_client_init( client_callbacks ) != SASL_OK ) { + nsldapi_initialized = 0; + pthread_mutex_unlock( &nsldapi_init_mutex ); + return; + } +#endif + +#if defined( STR_TRANSLATION ) && defined( LDAP_DEFAULT_CHARSET ) + nsldapi_ld_defaults.ld_lberoptions |= LBER_OPT_TRANSLATE_STRINGS; +#if LDAP_CHARSET_8859 == LDAP_DEFAULT_CHARSET + ldap_set_string_translators( &nsldapi_ld_defaults, ldap_8859_to_t61, + ldap_t61_to_8859 ); +#endif /* LDAP_CHARSET_8859 == LDAP_DEFAULT_CHARSET */ +#endif /* STR_TRANSLATION && LDAP_DEFAULT_CHARSET */ + + /* set default connect timeout (in milliseconds) */ + /* this was picked as it is the standard tcp timeout as well */ + nsldapi_ld_defaults.ld_connect_timeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; + +#if defined(USE_PTHREADS) || defined(_WINDOWS) + /* load up default platform specific locking routines */ + if (ldap_set_option( &nsldapi_ld_defaults, LDAP_OPT_THREAD_FN_PTRS, + (void *)&nsldapi_default_thread_fns) != LDAP_SUCCESS) { + nsldapi_initialized = 0; + pthread_mutex_unlock( &nsldapi_init_mutex ); + return; + } + +#ifndef _WINDOWS + /* load up default threadid function */ + if (ldap_set_option( &nsldapi_ld_defaults, LDAP_OPT_EXTRA_THREAD_FN_PTRS, + (void *)&nsldapi_default_extra_thread_fns) != LDAP_SUCCESS) { + nsldapi_initialized = 0; + pthread_mutex_unlock( &nsldapi_init_mutex ); + return; + } +#endif /* _WINDOWS */ + nsldapi_initialized = 1; + pthread_mutex_unlock( &nsldapi_init_mutex ); +#else + nsldapi_initialized = 1; +#endif /* use_pthreads || _windows */ +} + + +/* + * ldap_version - report version levels for important properties + * This function is deprecated. Use ldap_get_option( ..., LDAP_OPT_API_INFO, + * ... ) instead. + * + * Example: + * LDAPVersion ver; + * ldap_version( &ver ); + * if ( (ver.sdk_version < 100) || (ver.SSL_version < 300) ) + * fprintf( stderr, "LDAP SDK level insufficient\n" ); + * + * or: + * if ( ldap_version(NULL) < 100 ) + * fprintf( stderr, "LDAP SDK level insufficient\n" ); + * + */ + +int +LDAP_CALL +ldap_version( LDAPVersion *ver ) +{ + if ( NULL != ver ) + { + memset( ver, 0, sizeof(*ver) ); + ver->sdk_version = (int)(VI_PRODUCTVERSION * 100); + ver->protocol_version = LDAP_VERSION_MAX * 100; + ver->SSL_version = SSL_VERSION * 100; + /* + * set security to none by default + */ + + ver->security_level = LDAP_SECURITY_NONE; +#if defined(LINK_SSL) +#if defined(NS_DOMESTIC) + ver->security_level = 128; +#elif defined(NSS_EXPORT) + ver->security_level = 40; +#endif +#endif + + } + return (int)(VI_PRODUCTVERSION * 100); +} + +/* + * ldap_open - initialize and connect to an ldap server. A magic cookie to + * be used for future communication is returned on success, NULL on failure. + * "host" may be a space-separated list of hosts or IP addresses + * + * Example: + * LDAP *ld; + * ld = ldap_open( hostname, port ); + */ + +LDAP * +LDAP_CALL +ldap_open( const char *host, int port ) +{ + LDAP *ld; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_open\n", 0, 0, 0 ); + + if (( ld = ldap_init( host, port )) == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + if ( nsldapi_open_ldap_defconn( ld ) < 0 ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + ldap_ld_free( ld, NULL, NULL, 0 ); + return( NULL ); + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_open successful, ld_host is %s\n", + ( ld->ld_host == NULL ) ? "(null)" : ld->ld_host, 0, 0 ); + + return( ld ); +} + + +/* + * ldap_init - initialize the LDAP library. A magic cookie to be used for + * future communication is returned on success, NULL on failure. + * "defhost" may be a space-separated list of hosts or IP addresses + * + * NOTE: If you want to use IPv6, you must use prldap creating a LDAP handle + * with prldap_init instead of ldap_init. Or install the NSPR functions + * by calling prldap_install_routines. (See the nspr samples in examples) + * + * Example: + * LDAP *ld; + * ld = ldap_init( default_hostname, default_port ); + */ +LDAP * +LDAP_CALL +ldap_init( const char *defhost, int defport ) +{ + LDAP *ld; + + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + + if ( defport < 0 || defport > LDAP_PORT_MAX ) { + LDAPDebug( LDAP_DEBUG_ANY, + "ldap_init: port %d is invalid (port numbers must range from 1 to %d)\n", + defport, LDAP_PORT_MAX, 0 ); +#if !defined( macintosh ) && !defined( DOS ) && !defined( BEOS ) + errno = EINVAL; +#endif + return( NULL ); + } + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_init\n", 0, 0, 0 ); + + if ( (ld = (LDAP*)NSLDAPI_MALLOC( sizeof(struct ldap) )) == NULL ) { + return( NULL ); + } + + /* copy defaults */ + SAFEMEMCPY( ld, &nsldapi_ld_defaults, sizeof( struct ldap )); + if ( nsldapi_ld_defaults.ld_io_fns_ptr != NULL ) { + if (( ld->ld_io_fns_ptr = (struct ldap_io_fns *)NSLDAPI_MALLOC( + sizeof( struct ldap_io_fns ))) == NULL ) { + NSLDAPI_FREE( (char *)ld ); + return( NULL ); + } + /* struct copy */ + *(ld->ld_io_fns_ptr) = *(nsldapi_ld_defaults.ld_io_fns_ptr); + } + + /* call the new handle I/O callback if one is defined */ + if ( ld->ld_extnewhandle_fn != NULL ) { + /* + * We always pass the session extended I/O argument to + * the new handle callback. + */ + if ( ld->ld_extnewhandle_fn( ld, ld->ld_ext_session_arg ) + != LDAP_SUCCESS ) { + NSLDAPI_FREE( (char*)ld ); + return( NULL ); + } + } + + /* allocate session-specific resources */ + if (( ld->ld_sbp = ber_sockbuf_alloc()) == NULL || + ( defhost != NULL && + ( ld->ld_defhost = nsldapi_strdup( defhost )) == NULL ) || + ((ld->ld_mutex = (void **) NSLDAPI_CALLOC( LDAP_MAX_LOCK, sizeof(void *))) == NULL )) { + if ( ld->ld_sbp != NULL ) { + ber_sockbuf_free( ld->ld_sbp ); + } + if( ld->ld_mutex != NULL ) { + NSLDAPI_FREE( ld->ld_mutex ); + } + NSLDAPI_FREE( (char*)ld ); + return( NULL ); + } + + /* install Sockbuf I/O functions if set in LDAP * */ + if ( ld->ld_extread_fn != NULL || ld->ld_extwrite_fn != NULL ) { + struct lber_x_ext_io_fns lberiofns; + + memset( &lberiofns, 0, sizeof( lberiofns )); + + lberiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + lberiofns.lbextiofn_read = ld->ld_extread_fn; + lberiofns.lbextiofn_write = ld->ld_extwrite_fn; + lberiofns.lbextiofn_writev = ld->ld_extwritev_fn; + lberiofns.lbextiofn_socket_arg = NULL; + ber_sockbuf_set_option( ld->ld_sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&lberiofns ); + } + + /* allocate mutexes */ + nsldapi_mutex_alloc_all( ld ); + + /* set default port */ + ld->ld_defport = ( defport == 0 ) ? LDAP_PORT : defport; + + return( ld ); +} + + +void +nsldapi_mutex_alloc_all( LDAP *ld ) +{ + int i; + + if ( ld != &nsldapi_ld_defaults && ld->ld_mutex != NULL ) { + for ( i = 0; ild_mutex[i] = LDAP_MUTEX_ALLOC( ld ); + ld->ld_mutex_threadid[i] = (void *) -1; + ld->ld_mutex_refcnt[i] = 0; + } + } +} + + +void +nsldapi_mutex_free_all( LDAP *ld ) +{ + int i; + + if ( ld != &nsldapi_ld_defaults && ld->ld_mutex != NULL ) { + for ( i = 0; ild_mutex[i] ); + } + } +} + + +/* returns 0 if connection opened and -1 if an error occurs */ +int +nsldapi_open_ldap_defconn( LDAP *ld ) +{ + LDAPServer *srv; + + if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer ))) == + NULL || ( ld->ld_defhost != NULL && ( srv->lsrv_host = + nsldapi_strdup( ld->ld_defhost )) == NULL )) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + srv->lsrv_port = ld->ld_defport; + + if (( ld->ld_options & LDAP_BITOPT_SSL ) != 0 ) { + srv->lsrv_options |= LDAP_SRV_OPT_SECURE; + } + + if (( ld->ld_defconn = nsldapi_new_connection( ld, &srv, 1, 1, 0 )) + == NULL ) { + if ( ld->ld_defhost != NULL ) { + NSLDAPI_FREE( srv->lsrv_host ); + } + NSLDAPI_FREE( (char *)srv ); + return( -1 ); + } + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + + return( 0 ); +} + + +struct ldap_x_hostlist_status { + char *lhs_hostlist; + char *lhs_nexthost; + int lhs_defport; +}; + +/* + * Return the first host and port in hostlist (setting *hostp and *portp). + * Return value is an LDAP API error code (LDAP_SUCCESS if all goes well). + * Note that a NULL or zero-length hostlist causes the host "127.0.0.1" to + * be returned. + */ +int LDAP_CALL +ldap_x_hostlist_first( const char *hostlist, int defport, char **hostp, + int *portp, struct ldap_x_hostlist_status **statusp ) +{ + + if ( NULL == hostp || NULL == portp || NULL == statusp ) { + return( LDAP_PARAM_ERROR ); + } + + if ( NULL == hostlist || *hostlist == '\0' ) { + *hostp = nsldapi_strdup( "127.0.0.1" ); + if ( NULL == *hostp ) { + return( LDAP_NO_MEMORY ); + } + *portp = defport; + *statusp = NULL; + return( LDAP_SUCCESS ); + } + + *statusp = NSLDAPI_CALLOC( 1, sizeof( struct ldap_x_hostlist_status )); + if ( NULL == *statusp ) { + return( LDAP_NO_MEMORY ); + } + (*statusp)->lhs_hostlist = nsldapi_strdup( hostlist ); + if ( NULL == (*statusp)->lhs_hostlist ) { + return( LDAP_NO_MEMORY ); + } + (*statusp)->lhs_nexthost = (*statusp)->lhs_hostlist; + (*statusp)->lhs_defport = defport; + return( ldap_x_hostlist_next( hostp, portp, *statusp )); +} + +/* + * Return the next host and port in hostlist (setting *hostp and *portp). + * Return value is an LDAP API error code (LDAP_SUCCESS if all goes well). + * If no more hosts are available, LDAP_SUCCESS is returned but *hostp is set + * to NULL. + */ +int LDAP_CALL +ldap_x_hostlist_next( char **hostp, int *portp, + struct ldap_x_hostlist_status *status ) +{ + char *q; + int squarebrackets = 0; + + if ( NULL == hostp || NULL == portp ) { + return( LDAP_PARAM_ERROR ); + } + + if ( NULL == status || NULL == status->lhs_nexthost ) { + *hostp = NULL; + return( LDAP_SUCCESS ); + } + + /* + * skip past leading '[' if present (IPv6 addresses may be surrounded + * with square brackets, e.g., [fe80::a00:20ff:fee5:c0b4]:389 + */ + if ( status->lhs_nexthost[0] == '[' ) { + ++status->lhs_nexthost; + squarebrackets = 1; + } + + /* copy host into *hostp */ + if ( NULL != ( q = strchr( status->lhs_nexthost, ' ' ))) { + size_t len = q - status->lhs_nexthost; + *hostp = NSLDAPI_MALLOC( len + 1 ); + if ( NULL == *hostp ) { + return( LDAP_NO_MEMORY ); + } + strncpy( *hostp, status->lhs_nexthost, len ); + (*hostp)[len] = '\0'; + status->lhs_nexthost += ( len + 1 ); + } else { /* last host */ + *hostp = nsldapi_strdup( status->lhs_nexthost ); + if ( NULL == *hostp ) { + return( LDAP_NO_MEMORY ); + } + status->lhs_nexthost = NULL; + } + + /* + * Look for closing ']' and skip past it before looking for port. + */ + if ( squarebrackets && NULL != ( q = strchr( *hostp, ']' ))) { + *q++ = '\0'; + } else { + q = *hostp; + } + + /* determine and set port */ + if ( NULL != ( q = strchr( q, ':' ))) { + *q++ = '\0'; + *portp = atoi( q ); + } else { + *portp = status->lhs_defport; + } + + return( LDAP_SUCCESS ); +} + + +void LDAP_CALL +ldap_x_hostlist_statusfree( struct ldap_x_hostlist_status *status ) +{ + if ( NULL != status ) { + if ( NULL != status->lhs_hostlist ) { + NSLDAPI_FREE( status->lhs_hostlist ); + } + NSLDAPI_FREE( status ); + } +} + + + +/* + * memory allocation functions. we include these in open.c since every + * LDAP application is likely to pull the rest of the code in this file + * in anyways. + */ +void * +ldap_x_malloc( size_t size ) +{ + return( nsldapi_memalloc_fns.ldapmem_malloc == NULL ? + malloc( size ) : + nsldapi_memalloc_fns.ldapmem_malloc( size )); +} + + +void * +ldap_x_calloc( size_t nelem, size_t elsize ) +{ + return( nsldapi_memalloc_fns.ldapmem_calloc == NULL ? + calloc( nelem, elsize ) : + nsldapi_memalloc_fns.ldapmem_calloc( nelem, elsize )); +} + + +void * +ldap_x_realloc( void *ptr, size_t size ) +{ + return( nsldapi_memalloc_fns.ldapmem_realloc == NULL ? + realloc( ptr, size ) : + nsldapi_memalloc_fns.ldapmem_realloc( ptr, size )); +} + + +void +ldap_x_free( void *ptr ) +{ + if ( nsldapi_memalloc_fns.ldapmem_free == NULL ) { + free( ptr ); + } else { + nsldapi_memalloc_fns.ldapmem_free( ptr ); + } +} + + +/* if s is NULL, returns NULL */ +char * +nsldapi_strdup( const char *s ) +{ + char *p; + + if ( s == NULL || + (p = (char *)NSLDAPI_MALLOC( strlen( s ) + 1 )) == NULL ) + return( NULL ); + + strcpy( p, s ); + + return( p ); +} diff --git a/ldap/c-sdk/libraries/libldap/os-ip.c b/ldap/c-sdk/libraries/libldap/os-ip.c new file mode 100644 index 0000000000..2e70139be3 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/os-ip.c @@ -0,0 +1,1862 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * os-ip.c -- platform-specific TCP & UDP related code + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +/* + * On platforms where poll() does not exist, we use select(). + * Therefore, we should increase the number of file descriptors + * we can use by #defining FD_SETSIZE to a large number before + * we include or its equivalent. We do not need + * to do this for Windows, because on that platform there is no + * relationship between FD_SETSIZE and the highest numbered file + * descriptor we can use. See the document fdsetsize.txt in + * this directory for a description of the setting of FD_SETSIZE + * for the OS'es we care about. + */ +#ifndef NSLDAPI_HAVE_POLL +/* XXX value for BSDI? */ +/* XXX value for macintosh (if applicable)? */ +#endif + +#include "ldap-int.h" +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +#include +#endif + +#ifdef NSLDAPI_HAVE_POLL +#include +#endif + + +#ifdef _WINDOWS +#define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) == INVALID_SOCKET) +#else +#define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) < 0 ) +#endif + + +#define NSLDAPI_POLL_ARRAY_GROWTH 5 /* grow arrays 5 elements at a time */ + + +/* + * Structures and union for tracking status of network sockets + */ +#ifdef NSLDAPI_HAVE_POLL +struct nsldapi_os_statusinfo { /* used with native OS poll() */ + struct pollfd *ossi_pollfds; + int ossi_pollfds_size; +}; +#else /* NSLDAPI_HAVE_POLL */ +struct nsldapi_os_statusinfo { /* used with native OS select() */ + fd_set ossi_readfds; + fd_set ossi_writefds; + fd_set ossi_use_readfds; + fd_set ossi_use_writefds; +}; +#endif /* else NSLDAPI_HAVE_POLL */ + +struct nsldapi_cb_statusinfo { /* used with ext. I/O poll() callback */ + LDAP_X_PollFD *cbsi_pollfds; + int cbsi_pollfds_size; +}; + +/* + * NSLDAPI_CB_POLL_MATCH() evaluates to non-zero (true) if the Sockbuf *sdp + * matches the LDAP_X_PollFD pollfd. + */ +#ifdef _WINDOWS +#define NSLDAPI_CB_POLL_SD_CAST (unsigned int) +#else +#define NSLDAPI_CB_POLL_SD_CAST +#endif +#define NSLDAPI_CB_POLL_MATCH( sbp, pollfd ) \ + ((sbp)->sb_sd == NSLDAPI_CB_POLL_SD_CAST ((pollfd).lpoll_fd) && \ + (sbp)->sb_ext_io_fns.lbextiofn_socket_arg == (pollfd).lpoll_socketarg) + + +struct nsldapi_iostatus_info { + int ios_type; +#define NSLDAPI_IOSTATUS_TYPE_OSNATIVE 1 /* poll() or select() */ +#define NSLDAPI_IOSTATUS_TYPE_CALLBACK 2 /* poll()-like */ + int ios_read_count; + int ios_write_count; + union { + struct nsldapi_os_statusinfo ios_osinfo; + struct nsldapi_cb_statusinfo ios_cbinfo; + } ios_status; +}; + +#ifndef NSLDAPI_AVOID_OS_SOCKETS +#ifdef NSLDAPI_HAVE_POLL +static int nsldapi_add_to_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short events ); +static int nsldapi_clear_from_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short events ); +static int nsldapi_find_in_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short revents ); +#endif /* NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + +static int nsldapi_iostatus_init_nolock( LDAP *ld ); +static int nsldapi_add_to_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ); +static int nsldapi_clear_from_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ); +static int nsldapi_find_in_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short revents ); + + +#ifdef irix +#ifndef _PR_THREADS +/* + * XXXmcs: on IRIX NSPR's poll() and select() wrappers will crash if NSPR + * has not been initialized. We work around the problem by bypassing + * the NSPR wrapper functions and going directly to the OS' functions. + */ +#define NSLDAPI_POLL _poll +#define NSLDAPI_SELECT _select +extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout); +extern int _select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +#else /* _PR_THREADS */ +#define NSLDAPI_POLL poll +#define NSLDAPI_SELECT select +#endif /* else _PR_THREADS */ +#else /* irix */ +#define NSLDAPI_POLL poll +#define NSLDAPI_SELECT select +#endif /* else irix */ + + +static LBER_SOCKET nsldapi_os_socket( LDAP *ld, int secure, int domain, + int type, int protocol ); +static int nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ); +static int nsldapi_os_connect_with_to( LBER_SOCKET s, struct sockaddr *name, + int namelen, int msec_timeout ); +#if defined(KERBEROS) +char * nsldapi_host_connected_to( LDAP *ld, Sockbuf *sb ); +#endif + +/* + * Function typedefs used by nsldapi_try_each_host() + */ +typedef LBER_SOCKET (NSLDAPI_SOCKET_FN)( LDAP *ld, int secure, int domain, + int type, int protocol ); +typedef int (NSLDAPI_IOCTL_FN)( LBER_SOCKET s, int option, int *statusp ); +typedef int (NSLDAPI_CONNECT_WITH_TO_FN )( LBER_SOCKET s, struct sockaddr *name, + int namelen, int msec_timeout ); +typedef int (NSLDAPI_CONNECT_FN )( LBER_SOCKET s, struct sockaddr *name, + int namelen ); +typedef int (NSLDAPI_CLOSE_FN )( LBER_SOCKET s ); + +static int nsldapi_try_each_host( LDAP *ld, const char *hostlist, int defport, + int secure, NSLDAPI_SOCKET_FN *socketfn, NSLDAPI_IOCTL_FN *ioctlfn, + NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, + NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ); + + +static int +nsldapi_os_closesocket( LBER_SOCKET s ) +{ + int rc; + +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef _WINDOWS + rc = closesocket( s ); +#else /* _WINDOWS */ + rc = close( s ); +#endif /* _WINDOWS */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + return( rc ); +} + + +static LBER_SOCKET +nsldapi_os_socket( LDAP *ld, int secure, int domain, int type, int protocol ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int s, invalid_socket; + char *errmsg = NULL; + + if ( secure ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, + nsldapi_strdup( "secure mode not supported" )); + return( -1 ); + } + + s = socket( domain, type, protocol ); + + /* + * if the socket() call failed or it returned a socket larger + * than we can deal with, return a "local error." + */ + if ( NSLDAPI_INVALID_OS_SOCKET( s )) { + errmsg = "unable to create a socket"; + invalid_socket = 1; + } else { /* valid socket -- check for overflow */ + invalid_socket = 0; +#if !defined(NSLDAPI_HAVE_POLL) && !defined(_WINDOWS) + /* not on Windows and do not have poll() */ + if ( s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#endif + } + + if ( errmsg != NULL ) { /* local socket error */ + if ( !invalid_socket ) { + nsldapi_os_closesocket( s ); + } + errmsg = nsldapi_strdup( errmsg ); + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, errmsg ); + return( -1 ); + } + + return( s ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + + +/* + * Non-blocking connect call function + */ +static int +nsldapi_os_connect_with_to(LBER_SOCKET sockfd, struct sockaddr *saptr, + int salen, int msec) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int n, error; + int len; +#if defined(_WINDOWS) || defined(XP_OS2) + int nonblock = 1; + int block = 0; +#else + int flags; +#endif /* _WINDOWS */ +#ifdef NSLDAPI_HAVE_POLL + struct pollfd pfd; +#else + struct timeval tval; + fd_set rset, wset; +#ifdef _WINDOWS + fd_set eset; +#endif +#endif /* NSLDAPI_HAVE_POLL */ + + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_nonblock timeout: %d (msec)\n", + msec, 0, 0); + +#ifdef _WINDOWS + ioctlsocket(sockfd, FIONBIO, &nonblock); +#elif defined(XP_OS2) + ioctl( sockfd, FIONBIO, &nonblock, sizeof(nonblock) ); +#else + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif /* _WINDOWS */ + + error = 0; + if ((n = connect(sockfd, saptr, salen)) < 0) +#ifdef _WINDOWS + if ((n != SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) { +#else + if (errno != EINPROGRESS) { +#endif /* _WINDOWS */ +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("connect"); + } +#endif + return (-1); + } + + /* success */ + if (n == 0) + goto done; + +#ifdef NSLDAPI_HAVE_POLL + pfd.fd = sockfd; + pfd.events = POLLOUT; +#else + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + wset = rset; + +#ifdef _WINDOWS + eset = rset; +#endif /* _WINDOWS */ +#endif /* NSLDAPI_HAVE_POLL */ + + if (msec < 0 && msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) { + LDAPDebug( LDAP_DEBUG_TRACE, "Invalid timeout value detected.." + "resetting connect timeout to default value " + "(LDAP_X_IO_TIMEOUT_NO_TIMEOUT\n", 0, 0, 0); + msec = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; +#ifndef NSLDAPI_HAVE_POLL + } else { + if (msec != 0) { + tval.tv_sec = msec / 1000; + tval.tv_usec = 1000 * ( msec % 1000 ); + } else { + tval.tv_sec = 0; + tval.tv_usec = 0; + } +#endif /* NSLDAPI_HAVE_POLL */ + } + +#ifdef NSLDAPI_HAVE_POLL + if ((n = poll(&pfd, 1, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? msec : -1)) == 0) { + errno = ETIMEDOUT; + return (-1); + } + if (pfd.revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); +#ifdef LDAP_DEBUG + } else if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("poll error: sockfd not set"); +#endif + } + +#else /* NSLDAPI_HAVE_POLL */ + /* if timeval structure == NULL, select will block indefinitely */ + /* != NULL, and value == 0, select will */ + /* not block */ + /* Windows is a bit quirky on how it behaves w.r.t nonblocking */ + /* connects. If the connect fails, the exception fd, eset, is */ + /* set to show the failure. The first argument in select is */ + /* ignored */ + +#ifdef _WINDOWS + if ((n = select(sockfd +1, &rset, &wset, &eset, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) { + errno = WSAETIMEDOUT; + return (-1); + } + /* if wset is set, the connect worked */ + if (FD_ISSET(sockfd, &wset) || FD_ISSET(sockfd, &rset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); + goto done; + } + + /* if eset is set, the connect failed */ + if (FD_ISSET(sockfd, &eset)) { + return (-1); + } + + /* failure on select call */ + if (n == SOCKET_ERROR) { + perror("select error: SOCKET_ERROR returned"); + return (-1); + } +#else + if ((n = select(sockfd +1, &rset, &wset, NULL, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) { + errno = ETIMEDOUT; + return (-1); + } + if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); +#ifdef LDAP_DEBUG + } else if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("select error: sockfd not set"); +#endif + } +#endif /* _WINDOWS */ +#endif /* NSLDAPI_HAVE_POLL */ + +done: +#ifdef _WINDOWS + ioctlsocket(sockfd, FIONBIO, &block); +#elif defined(XP_OS2) + ioctl( sockfd, FIONBIO, &nonblock, sizeof(block) ); +#else + fcntl(sockfd, F_SETFL, flags); +#endif /* _WINDOWS */ + + if (error) { + errno = error; + return (-1); + } + + return (0); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + +static int +nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int err; +#if defined(_WINDOWS) || defined(XP_OS2) + u_long iostatus; +#endif + + if ( FIONBIO != option ) { + return( -1 ); + } + +#ifdef _WINDOWS + iostatus = *(u_long *)statusp; + err = ioctlsocket( s, FIONBIO, &iostatus ); +#else +#ifdef XP_OS2 + err = ioctl( s, FIONBIO, (caddr_t)&iostatus, sizeof(iostatus) ); +#else + err = ioctl( s, FIONBIO, (caddr_t)statusp ); +#endif +#endif + + return( err ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + +int +nsldapi_connect_to_host( LDAP *ld, Sockbuf *sb, const char *hostlist, + int defport, int secure, char **krbinstancep ) +/* + * "defport" must be in host byte order + * zero is returned upon success, -1 if fatal error, -2 EINPROGRESS + * if -1 is returned, ld_errno is set + */ +{ + int s; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_to_host: %s, port: %d\n", + NULL == hostlist ? "NULL" : hostlist, defport, 0 ); + + /* + * If an extended I/O connect callback has been defined, just use it. + */ + if ( NULL != ld->ld_extconnect_fn ) { + unsigned long connect_opts = 0; + + if ( ld->ld_options & LDAP_BITOPT_ASYNC) { + connect_opts |= LDAP_X_EXTIOF_OPT_NONBLOCKING; + } + if ( secure ) { + connect_opts |= LDAP_X_EXTIOF_OPT_SECURE; + } + s = ld->ld_extconnect_fn( hostlist, defport, + ld->ld_connect_timeout, connect_opts, + ld->ld_ext_session_arg, + &sb->sb_ext_io_fns.lbextiofn_socket_arg ); + + } else { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + s = nsldapi_try_each_host( ld, hostlist, + defport, secure, nsldapi_os_socket, + nsldapi_os_ioctl, nsldapi_os_connect_with_to, + NULL, nsldapi_os_closesocket ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + } + + if ( s < 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); + return( -1 ); + } + + sb->sb_sd = s; + + /* + * Set krbinstancep (canonical name of host for use by Kerberos). + */ +#ifdef KERBEROS + char *p; + + if (( *krbinstancep = nsldapi_host_connected_to( sb )) != NULL + && ( p = strchr( *krbinstancep, '.' )) != NULL ) { + *p = '\0'; + } +#else /* KERBEROS */ + *krbinstancep = NULL; +#endif /* KERBEROS */ + + return( 0 ); +} + + +/* + * Returns a socket number if successful and -1 if an error occurs. + */ +static int +nsldapi_try_each_host( LDAP *ld, const char *hostlist, + int defport, int secure, NSLDAPI_SOCKET_FN *socketfn, + NSLDAPI_IOCTL_FN *ioctlfn, NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, + NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int rc = -1; + int s = 0; + int i, err, connected, use_hp; + int parse_err, port; + struct sockaddr_in sin; + nsldapi_in_addr_t address; + char **addrlist, *ldhpbuf, *ldhpbuf_allocd = NULL; + char *host; + LDAPHostEnt ldhent, *ldhp; + struct hostent *hp; + struct ldap_x_hostlist_status *status; +#ifdef GETHOSTBYNAME_BUF_T + GETHOSTBYNAME_BUF_T hbuf; + struct hostent hent; +#endif /* GETHOSTBYNAME_BUF_T */ + + connected = 0; + parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port, + &status ); + while ( !connected && LDAP_SUCCESS == parse_err && host != NULL ) { + ldhpbuf_allocd = NULL; + ldhp = NULL; + hp = NULL; + s = 0; + use_hp = 0; + addrlist = NULL; + + + if (( address = inet_addr( host )) == -1 ) { + if ( ld->ld_dns_gethostbyname_fn == NULL ) { +#ifdef GETHOSTBYNAME_R_RETURNS_INT + (void)GETHOSTBYNAME( host, &hent, hbuf, + sizeof(hbuf), &hp, &err ); +#else + hp = GETHOSTBYNAME( host, &hent, hbuf, + sizeof(hbuf), &err ); +#endif + if ( hp != NULL ) { + addrlist = hp->h_addr_list; + } + } else { + /* + * DNS callback installed... use it. + */ +#ifdef GETHOSTBYNAME_buf_t + /* avoid allocation by using hbuf if large enough */ + if ( sizeof( hbuf ) < ld->ld_dns_bufsize ) { + ldhpbuf = ldhpbuf_allocd + = NSLDAPI_MALLOC( ld->ld_dns_bufsize ); + } else { + ldhpbuf = (char *)hbuf; + } +#else /* GETHOSTBYNAME_buf_t */ + ldhpbuf = ldhpbuf_allocd = NSLDAPI_MALLOC( + ld->ld_dns_bufsize ); +#endif /* else GETHOSTBYNAME_buf_t */ + + if ( ldhpbuf == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, + NULL, NULL ); + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + + if (( ldhp = ld->ld_dns_gethostbyname_fn( host, + &ldhent, ldhpbuf, ld->ld_dns_bufsize, &err, + ld->ld_dns_extradata )) != NULL ) { + addrlist = ldhp->ldaphe_addr_list; + } + } + + if ( addrlist == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); + LDAP_SET_ERRNO( ld, EHOSTUNREACH ); /* close enough */ + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + use_hp = 1; + } + + rc = -1; + for ( i = 0; !use_hp || ( addrlist[ i ] != 0 ); i++ ) { + if ( -1 == ( s = (*socketfn)( ld, secure, AF_INET, + SOCK_STREAM, 0 ))) { + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + + if ( ld->ld_options & LDAP_BITOPT_ASYNC ) { + int iostatus = 1; + + err = (*ioctlfn)( s, FIONBIO, &iostatus ); + if ( err == -1 ) { + LDAPDebug( LDAP_DEBUG_ANY, + "FIONBIO ioctl failed on %d\n", + s, 0, 0 ); + } + } + + (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); + sin.sin_family = AF_INET; + sin.sin_port = htons( (unsigned short)port ); + + SAFEMEMCPY( (char *) &sin.sin_addr.s_addr, + ( use_hp ? (char *) addrlist[ i ] : + (char *) &address ), sizeof( sin.sin_addr.s_addr) ); + + { +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +/* + * Block all of the signals that might interrupt connect() since + * there is an OS bug that causes connect() to fail if it is restarted. + * Look in ../../include/portable.h for the definition of + * NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED. + */ + sigset_t ints_off, oldset; + + sigemptyset( &ints_off ); + sigaddset( &ints_off, SIGALRM ); + sigaddset( &ints_off, SIGIO ); + sigaddset( &ints_off, SIGCLD ); + + NSLDAPI_MT_SAFE_SIGPROCMASK( SIG_BLOCK, &ints_off, &oldset ); +#endif /* NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED */ + + if ( NULL != connectwithtofn ) { + err = (*connectwithtofn)(s, + (struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + ld->ld_connect_timeout); + } else { + err = (*connectfn)(s, + (struct sockaddr *)&sin, + sizeof(struct sockaddr_in)); + } +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +/* + * restore original signal mask + */ + NSLDAPI_MT_SAFE_SIGPROCMASK( SIG_SETMASK, &oldset, 0 ); +#endif /* NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED */ + + } + if ( err >= 0 ) { + connected = 1; + rc = 0; + break; + } else { + if ( ld->ld_options & LDAP_BITOPT_ASYNC) { +#ifdef _WINDOWS + if (err == -1 && WSAGetLastError() == WSAEWOULDBLOCK) + LDAP_SET_ERRNO( ld, EWOULDBLOCK ); +#endif /* _WINDOWS */ + err = LDAP_GET_ERRNO( ld ); + if ( NSLDAPI_ERRNO_IO_INPROGRESS( err )) { + LDAPDebug( LDAP_DEBUG_TRACE, "connect would block...\n", + 0, 0, 0 ); + rc = -2; + break; + } + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror( (char *)inet_ntoa( sin.sin_addr )); + } +#endif + (*closefn)( s ); + if ( !use_hp ) { + break; + } + } + } + + ldap_memfree( host ); + parse_err = ldap_x_hostlist_next( &host, &port, status ); + } + + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + + if ( connected ) { + LDAPDebug( LDAP_DEBUG_TRACE, "sd %d connected to: %s\n", + s, inet_ntoa( sin.sin_addr ), 0 ); + } + + return( rc == 0 ? s : -1 ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + +void +nsldapi_close_connection( LDAP *ld, Sockbuf *sb ) +{ + if ( ld->ld_extclose_fn == NULL ) { +#ifndef NSLDAPI_AVOID_OS_SOCKETS + nsldapi_os_closesocket( sb->sb_sd ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + } else { + ld->ld_extclose_fn( sb->sb_sd, + sb->sb_ext_io_fns.lbextiofn_socket_arg ); + } +} + + +#ifdef KERBEROS +char * +nsldapi_host_connected_to( LDAP *ld, Sockbuf *sb ) +{ + struct hostent *hp; + char *p; + int len; + struct sockaddr_in sin; + + (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); + len = sizeof( sin ); + if ( getpeername( sb->sb_sd, (struct sockaddr *)&sin, &len ) == -1 ) { + return( NULL ); + } + + /* + * do a reverse lookup on the addr to get the official hostname. + * this is necessary for kerberos to work right, since the official + * hostname is used as the kerberos instance. + */ +#error XXXmcs: need to use DNS callbacks here + if (( hp = (struct hostent *)gethostbyaddr( (char *) &sin.sin_addr, + sizeof( sin.sin_addr ), AF_INET )) != NULL ) { + if ( hp->h_name != NULL ) { + return( nsldapi_strdup( hp->h_name )); + } + } + + return( NULL ); +} +#endif /* KERBEROS */ + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_write( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_add_to_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLOUT )) { + ++iosp->ios_write_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( !FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds )) { + FD_SET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds ); + ++iosp->ios_write_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_add_to_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { + ++iosp->ios_write_count; + } + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_write: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_read( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_add_to_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLIN )) { + ++iosp->ios_read_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( !FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds )) { + FD_SET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds ); + ++iosp->ios_read_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_add_to_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { + ++iosp->ios_read_count; + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_read: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_clear( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLOUT )) { + --iosp->ios_write_count; + } + if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLIN )) { + --iosp->ios_read_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds )) { + FD_CLR( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds ); + --iosp->ios_write_count; + } + if ( FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds )) { + FD_CLR( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds ); + --iosp->ios_read_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_clear_from_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { + --iosp->ios_write_count; + } + if ( nsldapi_clear_from_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { + --iosp->ios_read_count; + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_clear: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Return a non-zero value if sb is ready for write. + */ +int +nsldapi_iostatus_is_write_ready( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + if ( iosp == NULL ) { + goto unlock_and_return; + } + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + /* + * if we are using poll() we do something a little tricky: if + * any bits in the socket's returned events field other than + * POLLIN (ready for read) are set, we return true. This + * is done so we notice when a server closes a connection + * or when another error occurs. The actual error will be + * noticed later when we call write() or send(). + */ + rc = nsldapi_find_in_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, ~POLLIN ); + +#else /* NSLDAPI_HAVE_POLL */ + rc = FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_use_writefds ); +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + rc = nsldapi_find_in_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLIN ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_is_write_ready: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Return a non-zero value if sb is ready for read. + */ +int +nsldapi_iostatus_is_read_ready( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + if ( iosp == NULL ) { + goto unlock_and_return; + } + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + /* + * if we are using poll() we do something a little tricky: if + * any bits in the socket's returned events field other than + * POLLOUT (ready for write) are set, we return true. This + * is done so we notice when a server closes a connection + * or when another error occurs. The actual error will be + * noticed later when we call read() or recv(). + */ + rc = nsldapi_find_in_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, ~POLLOUT ); + +#else /* NSLDAPI_HAVE_POLL */ + rc = FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_use_readfds ); +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + rc = nsldapi_find_in_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLOUT ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_is_read_ready: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Allocate and initialize ld->ld_iostatus if not already done. + * Should be called with LDAP_IOSTATUS_LOCK locked. + * Returns 0 if all goes well and -1 if not (sets error in ld) + */ +static int +nsldapi_iostatus_init_nolock( LDAP *ld ) +{ + NSLDAPIIOStatus *iosp; + + if ( ld->ld_iostatus != NULL ) { + return( 0 ); + } + + if (( iosp = (NSLDAPIIOStatus *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPIIOStatus ))) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + + if ( ld->ld_extpoll_fn == NULL ) { + iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_OSNATIVE; +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifndef NSLDAPI_HAVE_POLL + FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_readfds ); + FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_writefds ); +#endif /* !NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else { + iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_CALLBACK; + } + + ld->ld_iostatus = iosp; + return( 0 ); +} + + +void +nsldapi_iostatus_free( LDAP *ld ) +{ + if ( ld == NULL ) { + return; + } + + + /* clean up classic I/O compatibility glue */ + if ( ld->ld_io_fns_ptr != NULL ) { + if ( ld->ld_ext_session_arg != NULL ) { + NSLDAPI_FREE( ld->ld_ext_session_arg ); + } + NSLDAPI_FREE( ld->ld_io_fns_ptr ); + } + + /* clean up I/O status tracking info. */ + if ( ld->ld_iostatus != NULL ) { + NSLDAPIIOStatus *iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_HAVE_POLL + if ( iosp->ios_status.ios_osinfo.ossi_pollfds + != NULL ) { + NSLDAPI_FREE( + iosp->ios_status.ios_osinfo.ossi_pollfds ); + } +#endif /* NSLDAPI_HAVE_POLL */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( iosp->ios_status.ios_cbinfo.cbsi_pollfds + != NULL ) { + NSLDAPI_FREE( + iosp->ios_status.ios_cbinfo.cbsi_pollfds ); + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_free: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + + NSLDAPI_FREE( iosp ); + } +} + + + +#if !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) +static int +nsldapi_get_select_table_size( void ) +{ + static int tblsize = 0; /* static */ + + if ( tblsize == 0 ) { +#if defined(_WINDOWS) || defined(XP_OS2) + tblsize = FOPEN_MAX; /* ANSI spec. */ +#else +#ifdef USE_SYSCONF + tblsize = sysconf( _SC_OPEN_MAX ); +#else /* USE_SYSCONF */ + tblsize = getdtablesize(); +#endif /* else USE_SYSCONF */ +#endif /* else _WINDOWS */ + + if ( tblsize >= FD_SETSIZE ) { + /* + * clamp value so we don't overrun the fd_set structure + */ + tblsize = FD_SETSIZE - 1; + } + } + + return( tblsize ); +} +#endif /* !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) */ + + +static int +nsldapi_tv2ms( struct timeval *tv ) +{ + if ( tv == NULL ) { + return( -1 ); /* infinite timout for poll() */ + } + + return( tv->tv_sec * 1000 + tv->tv_usec / 1000 ); +} + + +int +nsldapi_iostatus_poll( LDAP *ld, struct timeval *timeout ) +{ + int rc; + NSLDAPIIOStatus *iosp; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_iostatus_poll\n", 0, 0, 0 ); + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + + if ( iosp == NULL || + ( iosp->ios_read_count <= 0 && iosp->ios_write_count <= 0 )) { + rc = 0; /* simulate a timeout */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifndef NSLDAPI_AVOID_OS_SOCKETS +#ifdef NSLDAPI_HAVE_POLL + + rc = NSLDAPI_POLL( iosp->ios_status.ios_osinfo.ossi_pollfds, + iosp->ios_status.ios_osinfo.ossi_pollfds_size, + nsldapi_tv2ms( timeout )); + +#else /* NSLDAPI_HAVE_POLL */ + + /* two (potentially large) struct copies */ + iosp->ios_status.ios_osinfo.ossi_use_readfds + = iosp->ios_status.ios_osinfo.ossi_readfds; + iosp->ios_status.ios_osinfo.ossi_use_writefds + = iosp->ios_status.ios_osinfo.ossi_writefds; + +#ifdef HPUX9 + rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), + (int *)&iosp->ios_status.ios_osinfo.ossi_use_readfds + (int *)&iosp->ios_status.ios_osinfo.ossi_use_writefds, + NULL, timeout ); +#else + rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), + &iosp->ios_status.ios_osinfo.ossi_use_readfds, + &iosp->ios_status.ios_osinfo.ossi_use_writefds, + NULL, timeout ); +#endif /* else HPUX9 */ +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + /* + * We always pass the session extended I/O argument to + * the extended poll() callback. + */ + rc = ld->ld_extpoll_fn( + iosp->ios_status.ios_cbinfo.cbsi_pollfds, + iosp->ios_status.ios_cbinfo.cbsi_pollfds_size, + nsldapi_tv2ms( timeout ), ld->ld_ext_session_arg ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_poll: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; /* simulate a timeout (what else to do?) */ + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + +#if defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) +/* + * returns 1 if "fd" was added to pollfds. + * returns 1 if some of the bits in "events" were added to pollfds. + * returns 0 if no changes were made. + */ +static int +nsldapi_add_to_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short events ) +{ + int i, openslot; + + /* first we check to see if "fd" is already in our pollfds */ + openslot = -1; + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[ i ].fd == fd ) { + if (( pip->ossi_pollfds[ i ].events & events ) + != events ) { + pip->ossi_pollfds[ i ].events |= events; + return( 1 ); + } else { + return( 0 ); + } + } + if ( pip->ossi_pollfds[ i ].fd == -1 && openslot == -1 ) { + openslot = i; /* remember for later */ + } + } + + /* + * "fd" is not currently being poll'd on -- add to array. + * if we need to expand the pollfds array, we do it in increments of + * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). + */ + if ( openslot == -1 ) { + struct pollfd *newpollfds; + + if ( pip->ossi_pollfds_size == 0 ) { + newpollfds = (struct pollfd *)NSLDAPI_MALLOC( + NSLDAPI_POLL_ARRAY_GROWTH + * sizeof( struct pollfd )); + } else { + newpollfds = (struct pollfd *)NSLDAPI_REALLOC( + pip->ossi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH + + pip->ossi_pollfds_size) + * sizeof( struct pollfd )); + } + if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ + return( 0 ); + } + pip->ossi_pollfds = newpollfds; + openslot = pip->ossi_pollfds_size; + pip->ossi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; + for ( i = openslot + 1; i < pip->ossi_pollfds_size; ++i ) { + pip->ossi_pollfds[ i ].fd = -1; + pip->ossi_pollfds[ i ].events = + pip->ossi_pollfds[ i ].revents = 0; + } + } + pip->ossi_pollfds[ openslot ].fd = fd; + pip->ossi_pollfds[ openslot ].events = events; + pip->ossi_pollfds[ openslot ].revents = 0; + return( 1 ); +} + + +/* + * returns 1 if any "events" from "fd" were removed from pollfds + * returns 0 of "fd" wasn't in pollfds or if events did not overlap. + */ +static int +nsldapi_clear_from_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short events ) +{ + int i; + + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[i].fd == fd ) { + if (( pip->ossi_pollfds[ i ].events & events ) != 0 ) { + pip->ossi_pollfds[ i ].events &= ~events; + if ( pip->ossi_pollfds[ i ].events == 0 ) { + pip->ossi_pollfds[i].fd = -1; + } + return( 1 ); /* events overlap */ + } else { + return( 0 ); /* events do not overlap */ + } + } + } + + return( 0 ); /* "fd" was not found */ +} + + +/* + * returns 1 if any "revents" from "fd" were set in pollfds revents field. + * returns 0 if not. + */ +static int +nsldapi_find_in_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short revents ) +{ + int i; + + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[i].fd == fd ) { + if (( pip->ossi_pollfds[ i ].revents & revents ) != 0 ) { + return( 1 ); /* revents overlap */ + } else { + return( 0 ); /* revents do not overlap */ + } + } + } + + return( 0 ); /* "fd" was not found */ +} +#endif /* !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) */ + +/* + * returns 1 if "sb" was added to pollfds. + * returns 1 if some of the bits in "events" were added to pollfds. + * returns 0 if no changes were made. + */ +static int +nsldapi_add_to_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, + short events ) +{ + int i, openslot; + + /* first we check to see if "sb" is already in our pollfds */ + openslot = -1; + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_events & events ) + != events ) { + pip->cbsi_pollfds[ i ].lpoll_events |= events; + return( 1 ); + } else { + return( 0 ); + } + } + if ( pip->cbsi_pollfds[ i ].lpoll_fd == -1 && openslot == -1 ) { + openslot = i; /* remember for later */ + } + } + + /* + * "sb" is not currently being poll'd on -- add to array. + * if we need to expand the pollfds array, we do it in increments of + * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). + */ + if ( openslot == -1 ) { + LDAP_X_PollFD *newpollfds; + + if ( pip->cbsi_pollfds_size == 0 ) { + newpollfds = (LDAP_X_PollFD *)NSLDAPI_MALLOC( + NSLDAPI_POLL_ARRAY_GROWTH + * sizeof( LDAP_X_PollFD )); + } else { + newpollfds = (LDAP_X_PollFD *)NSLDAPI_REALLOC( + pip->cbsi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH + + pip->cbsi_pollfds_size) + * sizeof( LDAP_X_PollFD )); + } + if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ + return( 0 ); + } + pip->cbsi_pollfds = newpollfds; + openslot = pip->cbsi_pollfds_size; + pip->cbsi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; + for ( i = openslot + 1; i < pip->cbsi_pollfds_size; ++i ) { + pip->cbsi_pollfds[ i ].lpoll_fd = -1; + pip->cbsi_pollfds[ i ].lpoll_socketarg = NULL; + pip->cbsi_pollfds[ i ].lpoll_events = + pip->cbsi_pollfds[ i ].lpoll_revents = 0; + } + } + pip->cbsi_pollfds[ openslot ].lpoll_fd = sb->sb_sd; + pip->cbsi_pollfds[ openslot ].lpoll_socketarg = + sb->sb_ext_io_fns.lbextiofn_socket_arg; + pip->cbsi_pollfds[ openslot ].lpoll_events = events; + pip->cbsi_pollfds[ openslot ].lpoll_revents = 0; + return( 1 ); +} + + +/* + * returns 1 if any "events" from "sb" were removed from pollfds + * returns 0 of "sb" wasn't in pollfds or if events did not overlap. + */ +static int +nsldapi_clear_from_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ) +{ + int i; + + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_events + & events ) != 0 ) { + pip->cbsi_pollfds[ i ].lpoll_events &= ~events; + if ( pip->cbsi_pollfds[ i ].lpoll_events + == 0 ) { + pip->cbsi_pollfds[i].lpoll_fd = -1; + } + return( 1 ); /* events overlap */ + } else { + return( 0 ); /* events do not overlap */ + } + } + } + + return( 0 ); /* "sb" was not found */ +} + + +/* + * returns 1 if any "revents" from "sb" were set in pollfds revents field. + * returns 0 if not. + */ +static int +nsldapi_find_in_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, + short revents ) +{ + int i; + + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_revents + & revents ) != 0 ) { + return( 1 ); /* revents overlap */ + } else { + return( 0 ); /* revents do not overlap */ + } + } + } + + return( 0 ); /* "sb" was not found */ +} + + +/* + * Install read and write functions into lber layer / sb + */ +int +nsldapi_install_lber_extiofns( LDAP *ld, Sockbuf *sb ) +{ + struct lber_x_ext_io_fns lberiofns; + + if ( NULL != sb ) { + lberiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + lberiofns.lbextiofn_read = ld->ld_extread_fn; + lberiofns.lbextiofn_write = ld->ld_extwrite_fn; + lberiofns.lbextiofn_writev = ld->ld_extwritev_fn; + lberiofns.lbextiofn_socket_arg = ld->ld_ext_session_arg; + + if ( ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, + &lberiofns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + } + + return( LDAP_SUCCESS ); +} + + +/* + ****************************************************************************** + * One struct and several functions to bridge the gap between new extended + * I/O functions that are installed using ldap_set_option( ..., + * LDAP_OPT_EXTIO_FN_PTRS, ... ) and the original "classic" I/O functions + * (installed using LDAP_OPT_IO_FN_PTRS) follow. + * + * Our basic strategy is to use the new extended arg to hold a pointer to a + * structure that contains a pointer to the LDAP * (which contains pointers + * to the old functions so we can call them) as well as a pointer to an + * LBER_SOCKET to hold the socket used by the classic functions (the new + * functions use a simple int for the socket). + */ +typedef struct nsldapi_compat_socket_info { + LBER_SOCKET csi_socket; + LDAP *csi_ld; +} NSLDAPICompatSocketInfo; + +static int LDAP_CALLBACK +nsldapi_ext_compat_read( int s, void *buf, int len, + struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + + return( iofns->liof_read( csip->csi_socket, buf, len )); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_write( int s, const void *buf, int len, + struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + + return( iofns->liof_write( csip->csi_socket, buf, len )); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_poll( LDAP_X_PollFD fds[], int nfds, int timeout, + struct lextiof_session_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + fd_set readfds, writefds; + int i, rc, maxfd = 0; + struct timeval tv, *tvp; + + /* + * Prepare fd_sets for select() + */ + FD_ZERO( &readfds ); + FD_ZERO( &writefds ); + for ( i = 0; i < nfds; ++i ) { + if ( fds[ i ].lpoll_fd < 0 ) { + continue; + } + + if ( fds[ i ].lpoll_fd >= FD_SETSIZE ) { + LDAP_SET_ERRNO( csip->csi_ld, EINVAL ); + return( -1 ); + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN )) { + FD_SET( fds[i].lpoll_fd, &readfds ); + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT )) { + FD_SET( fds[i].lpoll_fd, &writefds ); + } + + fds[i].lpoll_revents = 0; /* clear revents */ + + if ( fds[i].lpoll_fd >= maxfd ) { + maxfd = fds[i].lpoll_fd; + } + } + + /* + * select() using callback. + */ + ++maxfd; + if ( timeout == -1 ) { + tvp = NULL; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = 1000 * ( timeout - tv.tv_sec * 1000 ); + tvp = &tv; + } + rc = iofns->liof_select( maxfd, &readfds, &writefds, NULL, tvp ); + if ( rc <= 0 ) { /* timeout or fatal error */ + return( rc ); + } + + /* + * Use info. in fd_sets to populate poll() revents. + */ + for ( i = 0; i < nfds; ++i ) { + if ( fds[ i ].lpoll_fd < 0 ) { + continue; + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN ) + && FD_ISSET( fds[i].lpoll_fd, &readfds )) { + fds[i].lpoll_revents |= LDAP_X_POLLIN; + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT ) + && FD_ISSET( fds[i].lpoll_fd, &writefds )) { + fds[i].lpoll_revents |= LDAP_X_POLLOUT; + } + + /* XXXmcs: any other cases to deal with? LDAP_X_POLLERR? */ + } + + return( rc ); +} + + +static LBER_SOCKET +nsldapi_compat_socket( LDAP *ld, int secure, int domain, int type, + int protocol ) +{ + int s; + + s = ld->ld_io_fns_ptr->liof_socket( domain, type, protocol ); + + if ( s >= 0 ) { + char *errmsg = NULL; + +#ifdef NSLDAPI_HAVE_POLL + if ( ld->ld_io_fns_ptr->liof_select != NULL + && s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#elif !defined(_WINDOWS) /* not on Windows and do not have poll() */ + if ( s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#endif + + if ( NULL == errmsg && secure && + ld->ld_io_fns_ptr->liof_ssl_enable( s ) < 0 ) { + errmsg = "failed to enable secure mode"; + } + + if ( NULL != errmsg ) { + if ( NULL == ld->ld_io_fns_ptr->liof_close ) { + nsldapi_os_closesocket( s ); + } else { + ld->ld_io_fns_ptr->liof_close( s ); + } + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, + nsldapi_strdup( errmsg )); + return( -1 ); + } + } + + return( s ); +} + + +/* + * Note: timeout is ignored because we have no way to pass it via + * the old I/O callback interface. + */ +static int LDAP_CALLBACK +nsldapi_ext_compat_connect( const char *hostlist, int defport, int timeout, + unsigned long options, struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ) +{ + NSLDAPICompatSocketInfo *defcsip; + struct ldap_io_fns *iofns; + int s, secure; + NSLDAPI_SOCKET_FN *socketfn; + NSLDAPI_IOCTL_FN *ioctlfn; + NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn; + NSLDAPI_CONNECT_FN *connectfn; + NSLDAPI_CLOSE_FN *closefn; + + defcsip = (NSLDAPICompatSocketInfo *)sessionarg; + iofns = defcsip->csi_ld->ld_io_fns_ptr; + + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { + if ( NULL == iofns->liof_ssl_enable ) { + LDAP_SET_ERRNO( defcsip->csi_ld, EINVAL ); + return( -1 ); + } + secure = 1; + } else { + secure = 0; + } + + socketfn = ( iofns->liof_socket == NULL ) ? + nsldapi_os_socket : nsldapi_compat_socket; + ioctlfn = ( iofns->liof_ioctl == NULL ) ? + nsldapi_os_ioctl : (NSLDAPI_IOCTL_FN *)(iofns->liof_ioctl); + if ( NULL == iofns->liof_connect ) { + connectwithtofn = nsldapi_os_connect_with_to; + connectfn = NULL; + } else { + connectwithtofn = NULL; + connectfn = iofns->liof_connect; + } + closefn = ( iofns->liof_close == NULL ) ? + nsldapi_os_closesocket : iofns->liof_close; + + s = nsldapi_try_each_host( defcsip->csi_ld, hostlist, defport, + secure, socketfn, ioctlfn, connectwithtofn, + connectfn, closefn ); + + if ( s >= 0 ) { + NSLDAPICompatSocketInfo *csip; + + if (( csip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { + (*closefn)( s ); + LDAP_SET_LDERRNO( defcsip->csi_ld, LDAP_NO_MEMORY, + NULL, NULL ); + return( -1 ); + } + + csip->csi_socket = s; + csip->csi_ld = defcsip->csi_ld; + *socketargp = (void *)csip; + + /* + * We always return 1, which is a valid but not unique socket + * (file descriptor) number. The extended I/O functions only + * require that the combination of the void *arg and the int + * socket be unique. Since we allocate the + * NSLDAPICompatSocketInfo that we assign to arg, we meet + * that requirement. + */ + s = 1; + } + + return( s ); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_close( int s, struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + int rc; + + rc = iofns->liof_close( csip->csi_socket ); + + NSLDAPI_FREE( csip ); + + return( rc ); +} + +/* + * Install the I/O functions. + * Return an LDAP error code (LDAP_SUCCESS if all goes well). + */ +int +nsldapi_install_compat_io_fns( LDAP *ld, struct ldap_io_fns *iofns ) +{ + NSLDAPICompatSocketInfo *defcsip; + + if (( defcsip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { + return( LDAP_NO_MEMORY ); + } + + defcsip->csi_socket = -1; + defcsip->csi_ld = ld; + + if ( ld->ld_io_fns_ptr != NULL ) { + (void)memset( (char *)ld->ld_io_fns_ptr, 0, + sizeof( struct ldap_io_fns )); + } else if (( ld->ld_io_fns_ptr = (struct ldap_io_fns *)NSLDAPI_CALLOC( + 1, sizeof( struct ldap_io_fns ))) == NULL ) { + NSLDAPI_FREE( defcsip ); + return( LDAP_NO_MEMORY ); + } + + /* struct copy */ + *(ld->ld_io_fns_ptr) = *iofns; + + ld->ld_extio_size = LBER_X_EXTIO_FNS_SIZE; + ld->ld_ext_session_arg = defcsip; + ld->ld_extread_fn = nsldapi_ext_compat_read; + ld->ld_extwrite_fn = nsldapi_ext_compat_write; + ld->ld_extpoll_fn = nsldapi_ext_compat_poll; + ld->ld_extconnect_fn = nsldapi_ext_compat_connect; + ld->ld_extclose_fn = nsldapi_ext_compat_close; + + return( nsldapi_install_lber_extiofns( ld, ld->ld_sbp )); +} +/* + * end of compat I/O functions + ****************************************************************************** + */ diff --git a/ldap/c-sdk/libraries/libldap/proxyauthctrl.c b/ldap/c-sdk/libraries/libldap/proxyauthctrl.c new file mode 100644 index 0000000000..e8e80cb144 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/proxyauthctrl.c @@ -0,0 +1,164 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +/* ldap_create_proxyauth_control + + Create a "version 1" proxied authorization control. + + Parameters are + + ld LDAP pointer to the desired connection + + dn The dn used in the proxy auth + + ctl_iscritical Indicates whether the control is critical of not. If + this field is non-zero, the operation will only be car- + ried out if the control is recognized by the server + and/or client + + ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_proxyauth_control ( + LDAP *ld, + const char *dn, + const char ctl_iscritical, + LDAPControl **ctrlp +) +{ + BerElement *ber; + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + if (NULL == dn) + { + dn = ""; + } + + /* create a ber package to hold the controlValue */ + if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + + + if ( LBER_ERROR == ber_printf( ber, + "{s}", + dn ) ) + { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_PROXYAUTH, ber, 1, + ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + +} + + +/* ldap_create_proxiedauth_control + + Create a "version 2" proxied authorization control. + + Parameters are + + ld LDAP pointer to the desired connection + + authzid The authorization identity used in the proxy auth, + e.g., dn:uid=bjensen,dc=example,dc=com + + ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_proxiedauth_control ( + LDAP *ld, + const char *authzid, + LDAPControl **ctrlp +) +{ + BerElement *ber; + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL || authzid == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + /* create a ber package to hold the controlValue */ + if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + + + if ( LBER_ERROR == ber_printf( ber, + "s", + authzid ) ) + { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_PROXIEDAUTH, ber, 1, 1, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + +} diff --git a/ldap/c-sdk/libraries/libldap/psearch.c b/ldap/c-sdk/libraries/libldap/psearch.c new file mode 100644 index 0000000000..71b9da90dd --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/psearch.c @@ -0,0 +1,190 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * psearch.c - Persistent search and "Entry Change Notification" support. + */ +#include "ldap-int.h" + + +int +LDAP_CALL +ldap_create_persistentsearch_control( LDAP *ld, int changetypes, + int changesonly, int return_echg_ctls, char ctl_iscritical, + LDAPControl **ctrlp ) +{ + BerElement *ber; + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL || ( changetypes & ~LDAP_CHANGETYPE_ANY ) != 0 ) { + rc = LDAP_PARAM_ERROR; + goto report_error_and_return; + } + + /* + * create a Persistent Search control. The control value looks like this: + * + * PersistentSearch ::= SEQUENCE { + * changeTypes INTEGER, + * -- the changeTypes field is the logical OR of + * -- one or more of these values: add (1), delete (2), + * -- modify (4), modDN (8). It specifies which types of + * -- changes will cause an entry to be returned. + * changesOnly BOOLEAN, -- skip initial search? + * returnECs BOOLEAN, -- return "Entry Change" controls? + * } + */ + if (( nsldapi_alloc_ber_with_options( ld, &ber )) != LDAP_SUCCESS ) { + rc = LDAP_NO_MEMORY; + goto report_error_and_return; + } + + if ( ber_printf( ber, "{ibb}", changetypes, changesonly, + return_echg_ctls ) == -1 ) { + ber_free( ber, 1 ); + rc = LDAP_ENCODING_ERROR; + goto report_error_and_return; + } + + rc = nsldapi_build_control( LDAP_CONTROL_PERSISTENTSEARCH, ber, 1, + ctl_iscritical, ctrlp ); + +report_error_and_return: + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + + +int +LDAP_CALL +ldap_parse_entrychange_control( LDAP *ld, LDAPControl **ctrls, ber_int_t *chgtypep, + char **prevdnp, int *chgnumpresentp, ber_int_t *chgnump ) +{ + BerElement *ber; + int rc, i; + ber_int_t changetype; + ber_len_t len; + ber_int_t along; + char *previousdn; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + /* + * find the entry change notification in the list of controls + */ + for ( i = 0; ctrls != NULL && ctrls[i] != NULL; ++i ) { + if ( strcmp( ctrls[i]->ldctl_oid, LDAP_CONTROL_ENTRYCHANGE ) == 0 ) { + break; + } + } + + if ( ctrls == NULL || ctrls[i] == NULL ) { + rc = LDAP_CONTROL_NOT_FOUND; + goto report_error_and_return; + } + + /* + * allocate a BER element from the control value and parse it. The control + * value should look like this: + * + * EntryChangeNotification ::= SEQUENCE { + * changeType ENUMERATED { + * add (1), -- these values match the + * delete (2), -- values used for changeTypes + * modify (4), -- in the PersistentSearch control. + * modDN (8), + * }, + * previousDN LDAPDN OPTIONAL, -- modDN ops. only + * changeNumber INTEGER OPTIONAL, -- if supported + * } + */ + if (( ber = ber_init( &(ctrls[i]->ldctl_value))) == NULL ) { + rc = LDAP_NO_MEMORY; + goto report_error_and_return; + } + + if ( ber_scanf( ber, "{e", &along ) == LBER_ERROR ) { + ber_free( ber, 1 ); + rc = LDAP_DECODING_ERROR; + goto report_error_and_return; + } + changetype = along; + + if ( changetype == LDAP_CHANGETYPE_MODDN ) { + if ( ber_scanf( ber, "a", &previousdn ) == LBER_ERROR ) { + ber_free( ber, 1 ); + rc = LDAP_DECODING_ERROR; + goto report_error_and_return; + } + } else { + previousdn = NULL; + } + + if ( chgtypep != NULL ) { + *chgtypep = changetype; + } + if ( prevdnp != NULL ) { + *prevdnp = previousdn; + } else if ( previousdn != NULL ) { + NSLDAPI_FREE( previousdn ); + } + + if ( chgnump != NULL ) { /* check for optional changenumber */ + if ( ber_peek_tag( ber, &len ) == LBER_INTEGER + && ber_get_int( ber, chgnump ) != LBER_ERROR ) { + if ( chgnumpresentp != NULL ) { + *chgnumpresentp = 1; + } + } else { + if ( chgnumpresentp != NULL ) { + *chgnumpresentp = 0; + } + } + } + + ber_free( ber, 1 ); + rc = LDAP_SUCCESS; + +report_error_and_return: + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libldap/pthreadtest.c b/ldap/c-sdk/libraries/libldap/pthreadtest.c new file mode 100644 index 0000000000..fa913f6919 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/pthreadtest.c @@ -0,0 +1,1028 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Authentication and search information. */ +#define NAME "cn=Directory Manager" +#define PASSWORD "rtfm11111" +#define BASE "dc=example,dc=com" +#define SCOPE LDAP_SCOPE_SUBTREE + +static void *modify_thread(); +static void *add_thread(); +static void *delete_thread(); +static void *bind_thread(); +static void *compare_thread(); +static void *search_thread(); +static void *my_mutex_alloc(); +static void my_mutex_free(); +void *my_sema_alloc( void ); +void my_sema_free( void * ); +int my_sema_wait( void * ); +int my_sema_post( void * ); +static void set_ld_error(); +static int get_ld_error(); +static void set_errno(); +static int get_errno(); +static void tsd_setup(); +static void tsd_cleanup(); +static int get_random_id( void ); +static char *get_id_str( int id ); + +/* Linked list of LDAPMessage structs for search results. */ +typedef struct ldapmsgwrapper { + LDAPMessage *lmw_messagep; + struct ldapmsgwrapper *lmw_next; +} ldapmsgwrapper; + +LDAP *ld; +pthread_key_t key; +int maxid = MAXINT; +int maxops = 0; /* zero means no limit */ +int range_filters = 0; /* if non-zero use >= and >= filters */ + +main( int argc, char **argv ) + +{ + pthread_attr_t attr; + pthread_t *threadids; + void *status; + struct ldap_thread_fns tfns; + struct ldap_extra_thread_fns extrafns; + int rc, c, errflg, i, inited_attr; + int doadd, dodelete, domodify, docompare, dosearch, dobind; + int option_extthreads, option_restart; + int each_thread_count, thread_count; + extern int optind; + extern char *optarg; + + doadd = dodelete = domodify = docompare = dobind = dosearch = 0; + option_extthreads = option_restart = 0; + inited_attr = 0; + errflg = 0; + each_thread_count = 1; /* how many of each type of thread? */ + rc = LDAP_SUCCESS; /* optimistic */ + + while (( c = getopt( argc, argv, "abcdmrsERi:n:o:S:" )) != EOF ) { + switch( c ) { + case 'a': /* perform add operations */ + ++doadd; + break; + case 'b': /* perform bind operations */ + ++dobind; + break; + case 'c': /* perform compare operations */ + ++docompare; + break; + case 'd': /* perform delete operations */ + ++dodelete; + break; + case 'm': /* perform modify operations */ + ++domodify; + break; + case 'r': /* use range filters in searches */ + ++range_filters; + break; + case 's': /* perform search operations */ + ++dosearch; + break; + case 'E': /* use extended thread functions */ + ++option_extthreads; + break; + case 'R': /* turn on LDAP_OPT_RESTART */ + ++option_restart; + break; + case 'i': /* highest # used for entry names */ + maxid = atoi( optarg ); + break; + case 'n': /* # threads for each operation */ + if (( each_thread_count = atoi( optarg )) < 1 ) { + fprintf( stderr, "thread count must be > 0\n" ); + ++errflg; + } + break; + case 'o': /* operations to perform per thread */ + if (( maxops = atoi( optarg )) < 0 ) { + fprintf( stderr, + "operation limit must be >= 0\n" ); + ++errflg; + } + break; + case 'S': /* random number seed */ + if ( *optarg == 'r' ) { + int seed = (int)time( (time_t *)0 ); + srandom( seed ); + printf( "Random seed: %d\n", seed ); + } else { + srandom( atoi( optarg )); + } + break; + default: + ++errflg; + break; + } + } + + /* Check command-line syntax. */ + thread_count = each_thread_count * ( doadd + dodelete + domodify + + dobind + docompare + dosearch ); + if ( thread_count < 1 ) { + fprintf( stderr, + "Specify at least one of -a, -b, -c, -d, -m, or -s\n" ); + ++errflg; + } + + if ( errflg || argc - optind != 2 ) { + fprintf( stderr, "usage: %s [-abcdmrsER] [-i id] [-n thr]" + " [-o oplim] [-S sd] host port\n", argv[0] ); + fputs( "\nWhere:\n" + "\t\"id\" is the highest entry id (name) to use" + " (default is MAXINT)\n" + "\t\"thr\" is the number of threads for each operation" + " (default is 1)\n" + "\t\"oplim\" is the number of ops done by each thread" + " (default is infinite)\n" + "\t\"sd\" is a random() number seed (default is 1). Use" + " the letter r for\n" + "\t\tsd to seed random() using time of day.\n" + "\t\"host\" is the hostname of an LDAP directory server\n" + "\t\"port\" is the TCP port of the LDAP directory server\n" + , stderr ); + fputs( "\nAnd the single character options are:\n" + "\t-a\tstart thread(s) to perform add operations\n" + "\t-b\tstart thread(s) to perform bind operations\n" + "\t-c\tstart thread(s) to perform compare operations\n" + "\t-d\tstart thread(s) to perform delete operations\n" + "\t-m\tstart thread(s) to perform modify operations\n" + "\t-s\tstart thread(s) to perform search operations\n" + "\t-r\tuse range filters in searches\n" + "\t-E\tinstall LDAP_OPT_EXTRA_THREAD_FN_PTRS\n" + "\t-R\tturn on LDAP_OPT_RESTART\n", stderr ); + + return( LDAP_PARAM_ERROR ); + } + + /* Create a key. */ + if ( pthread_key_create( &key, free ) != 0 ) { + perror( "pthread_key_create" ); + } + tsd_setup(); + + /* Allocate space for thread ids */ + if (( threadids = (pthread_t *)calloc( thread_count, + sizeof( pthread_t ))) == NULL ) { + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + + + /* Initialize the LDAP session. */ + if (( ld = ldap_init( argv[optind], atoi( argv[optind+1] ))) == NULL ) { + perror( "ldap_init" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + + /* Set the function pointers for dealing with mutexes + and error information. */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_mutex_alloc = (void *(*)(void)) my_mutex_alloc; + tfns.ltf_mutex_free = (void (*)(void *)) my_mutex_free; + tfns.ltf_mutex_lock = (int (*)(void *)) pthread_mutex_lock; + tfns.ltf_mutex_unlock = (int (*)(void *)) pthread_mutex_unlock; + tfns.ltf_get_errno = get_errno; + tfns.ltf_set_errno = set_errno; + tfns.ltf_get_lderrno = get_ld_error; + tfns.ltf_set_lderrno = set_ld_error; + tfns.ltf_lderrno_arg = NULL; + + /* Set up this session to use those function pointers. */ + + rc = ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *) &tfns ); + if ( rc < 0 ) { + rc = ldap_get_lderrno( ld, NULL, NULL ); + fprintf( stderr, + "ldap_set_option (LDAP_OPT_THREAD_FN_PTRS): %s\n", + ldap_err2string( rc ) ); + goto clean_up_and_return; + } + + if ( option_extthreads ) { + /* Set the function pointers for working with semaphores. */ + + memset( &extrafns, '\0', sizeof(struct ldap_extra_thread_fns) ); + extrafns.ltf_mutex_trylock = + (int (*)(void *)) pthread_mutex_trylock; + extrafns.ltf_sema_alloc = (void *(*)(void)) my_sema_alloc; + extrafns.ltf_sema_free = (void (*)(void *)) my_sema_free; + extrafns.ltf_sema_wait = (int (*)(void *)) my_sema_wait; + extrafns.ltf_sema_post = (int (*)(void *)) my_sema_post; + + /* Set up this session to use those function pointers. */ + + if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, + (void *) &extrafns ) != 0 ) { + rc = ldap_get_lderrno( ld, NULL, NULL ); + ldap_perror( ld, "ldap_set_option" + " (LDAP_OPT_EXTRA_THREAD_FN_PTRS)" ); + goto clean_up_and_return; + } + } + + + if ( option_restart && ldap_set_option( ld, LDAP_OPT_RESTART, + LDAP_OPT_ON ) != 0 ) { + rc = ldap_get_lderrno( ld, NULL, NULL ); + ldap_perror( ld, "ldap_set_option(LDAP_OPT_RESTART)" ); + goto clean_up_and_return; + } + + /* Attempt to bind to the server. */ + rc = ldap_simple_bind_s( ld, NAME, PASSWORD ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_simple_bind_s: %s\n", + ldap_err2string( rc ) ); + goto clean_up_and_return; + } + + /* Initialize the attribute. */ + if ( pthread_attr_init( &attr ) != 0 ) { + perror( "pthread_attr_init" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++inited_attr; + + /* Specify that the threads are joinable. */ + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + + /* Create all the requested threads */ + thread_count = 0; + if ( domodify ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + modify_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create modify_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + if ( doadd ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + add_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create add_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + if ( dodelete ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + delete_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create delete_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + if ( dobind ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + bind_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create bind_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + if ( docompare ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + compare_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create compare_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + if ( dosearch ) { + for ( i = 0; i < each_thread_count; ++i ) { + if ( pthread_create( &threadids[thread_count], &attr, + search_thread, get_id_str(thread_count) ) != 0 ) { + perror( "pthread_create search_thread" ); + rc = LDAP_LOCAL_ERROR; + goto clean_up_and_return; + } + ++thread_count; + } + } + + /* Wait until these threads exit. */ + for ( i = 0; i < thread_count; ++i ) { + pthread_join( threadids[i], &status ); + } + +clean_up_and_return: + if ( ld != NULL ) { + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + ldap_unbind( ld ); + } + if ( threadids != NULL ) { + free( threadids ); + } + if ( inited_attr ) { + pthread_attr_destroy( &attr ); + } + tsd_cleanup(); + + return( rc ); +} + + +static void * +modify_thread( char *id ) +{ + LDAPMessage *res; + LDAPMessage *e; + int i, modentry, num_entries, msgid, parse_rc, finished; + int rc, opcount; + LDAPMod mod; + LDAPMod *mods[2]; + char *vals[2]; + char *dn; + ldapmsgwrapper *list, *lmwp, *lastlmwp; + struct timeval zerotime; + void *voidrc = (void *)0; + + zerotime.tv_sec = zerotime.tv_usec = 0L; + + printf( "Starting modify_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + rc = ldap_search_ext( ld, BASE, SCOPE, "(objectclass=*)", + NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msgid ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: Modify thread: " + "ldap_search_ext: %s\n", id, ldap_err2string( rc ) ); + exit( 1 ); + } + list = lastlmwp = NULL; + finished = 0; + num_entries = 0; + while ( !finished ) { + rc = ldap_result( ld, msgid, LDAP_MSG_ONE, &zerotime, &res ); + switch ( rc ) { + case -1: + rc = ldap_get_lderrno( ld, NULL, NULL ); + fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) ); + exit( 1 ); + break; + case 0: + break; + /* Keep track of the number of entries found. */ + case LDAP_RES_SEARCH_ENTRY: + num_entries++; + if (( lmwp = (ldapmsgwrapper *) + malloc( sizeof( ldapmsgwrapper ))) == NULL ) { + fprintf( stderr, "Thread %s: Modify thread: Cannot malloc\n", id ); + exit( 1 ); + } + lmwp->lmw_messagep = res; + lmwp->lmw_next = NULL; + if ( lastlmwp == NULL ) { + list = lastlmwp = lmwp; + } else { + lastlmwp->lmw_next = lmwp; + } + lastlmwp = lmwp; + break; + case LDAP_RES_SEARCH_REFERENCE: + break; + case LDAP_RES_SEARCH_RESULT: + finished = 1; + parse_rc = ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); + if ( parse_rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: can't parse result code.\n", id ); + exit( 1 ); + } else { + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: ldap_search: %s\n", id, ldap_err2string( rc ) ); + } else { + printf( "Thread %s: Got %d results.\n", id, num_entries ); + } + } + break; + default: + break; + } + } + + mods[0] = &mod; + mods[1] = NULL; + vals[0] = "bar"; + vals[1] = NULL; + + for ( ;; ) { + modentry = random() % num_entries; + for ( i = 0, lmwp = list; lmwp != NULL && i < modentry; + i++, lmwp = lmwp->lmw_next ) { + /* NULL */ + } + + if ( lmwp == NULL ) { + fprintf( stderr, + "Thread %s: Modify thread could not find entry %d of %d\n", + id, modentry, num_entries ); + continue; + } + + e = lmwp->lmw_messagep; + printf( "Thread %s: Modify thread picked entry %d of %d\n", id, i, num_entries ); + dn = ldap_get_dn( ld, e ); + + mod.mod_op = LDAP_MOD_REPLACE; + mod.mod_type = "description"; + mod.mod_values = vals; + printf( "Thread %s: Modifying (%s)\n", id, dn ); + + rc = ldap_modify_ext_s( ld, dn, mods, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_modify_ext_s: %s\n", + ldap_err2string( rc ) ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_modify_ext_s" ); + voidrc = (void *)1; + goto modify_cleanup_and_return; + } + } + free( dn ); + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +modify_cleanup_and_return: + printf( "Thread %s: attempted %d modify operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +add_thread( char *id ) +{ + LDAPMod mod[4]; + LDAPMod *mods[5]; + char dn[BUFSIZ], name[40]; + char *cnvals[2], *snvals[2], *pwdvals[2], *ocvals[3]; + int i, rc, opcount; + void *voidrc = (void *)0; + + printf( "Starting add_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + for ( i = 0; i < 4; i++ ) { + mods[i] = &mod[i]; + } + + mods[4] = NULL; + + mod[0].mod_op = 0; + mod[0].mod_type = "cn"; + mod[0].mod_values = cnvals; + cnvals[1] = NULL; + mod[1].mod_op = 0; + mod[1].mod_type = "sn"; + mod[1].mod_values = snvals; + snvals[1] = NULL; + mod[2].mod_op = 0; + mod[2].mod_type = "objectclass"; + mod[2].mod_values = ocvals; + ocvals[0] = "top"; + ocvals[1] = "person"; + ocvals[2] = NULL; + mod[3].mod_op = 0; + mod[3].mod_type = "userPassword"; + mod[3].mod_values = pwdvals; + pwdvals[1] = NULL; + mods[4] = NULL; + + for ( ;; ) { + sprintf( name, "%d", get_random_id() ); + sprintf( dn, "cn=%s, " BASE, name ); + cnvals[0] = name; + snvals[0] = name; + pwdvals[0] = name; + + printf( "Thread %s: Adding entry (%s)\n", id, dn ); + rc = ldap_add_ext_s( ld, dn, mods, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_add_ext_s: %s\n", + ldap_err2string( rc ) ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_add_ext_s" ); + voidrc = (void *)1; + goto add_cleanup_and_return; + } + } + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +add_cleanup_and_return: + printf( "Thread %s: attempted %d add operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +delete_thread( char *id ) +{ + LDAPMessage *res; + char dn[BUFSIZ], name[40]; + int num_entries, msgid, rc, parse_rc, finished, opcount; + struct timeval zerotime; + void *voidrc = (void *)0; + + zerotime.tv_sec = zerotime.tv_usec = 0L; + + printf( "Starting delete_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + rc = ldap_search_ext( ld, BASE, SCOPE, "(objectclass=*)", + NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msgid ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: Delete thread: " + "ldap_search_ext: %s\n", id, ldap_err2string( rc ) ); + exit( 1 ); + } + + finished = 0; + num_entries = 0; + while ( !finished ) { + rc = ldap_result( ld, msgid, LDAP_MSG_ONE, &zerotime, &res ); + switch ( rc ) { + case -1: + rc = ldap_get_lderrno( ld, NULL, NULL ); + fprintf( stderr, "ldap_result: %s\n", ldap_err2string( rc ) ); + exit( 1 ); + break; + case 0: + break; + /* Keep track of the number of entries found. */ + case LDAP_RES_SEARCH_ENTRY: + num_entries++; + break; + case LDAP_RES_SEARCH_REFERENCE: + break; + case LDAP_RES_SEARCH_RESULT: + finished = 1; + parse_rc = ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 ); + if ( parse_rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: can't parse result code.\n", id ); + exit( 1 ); + } else { + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "Thread %s error: ldap_search: %s\n", id, ldap_err2string( rc ) ); + } else { + printf( "Thread %s: Got %d results.\n", id, num_entries ); + } + } + break; + default: + break; + } + } + + for ( ;; ) { + sprintf( name, "%d", get_random_id() ); + sprintf( dn, "cn=%s, " BASE, name ); + printf( "Thread %s: Deleting entry (%s)\n", id, dn ); + + if (( rc = ldap_delete_ext_s( ld, dn, NULL, NULL )) + != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_delete_ext_s" ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_delete_ext_s" ); + voidrc = (void *)1; + goto delete_cleanup_and_return; + } + } + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +delete_cleanup_and_return: + printf( "Thread %s: attempted %d delete operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +bind_thread( char *id ) +{ + char dn[BUFSIZ], name[40]; + int rc, opcount; + void *voidrc = (void *)0; + + printf( "Starting bind_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + for ( ;; ) { + sprintf( name, "%d", get_random_id() ); + sprintf( dn, "cn=%s, " BASE, name ); + printf( "Thread %s: Binding as entry (%s)\n", id, dn ); + + if (( rc = ldap_simple_bind_s( ld, dn, name )) + != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_simple_bind_s" ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_simple_bind_s" ); + voidrc = (void *)1; + goto bind_cleanup_and_return; + } + } else { + printf( "Thread %s: bound as entry (%s)\n", id, dn ); + } + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +bind_cleanup_and_return: + printf( "Thread %s: attempted %d bind operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +compare_thread( char *id ) +{ + char dn[BUFSIZ], name[40], cmpval[40]; + int rc, randval, opcount; + struct berval bv; + void *voidrc = (void *)0; + + printf( "Starting compare_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + for ( ;; ) { + randval = get_random_id(); + sprintf( name, "%d", randval ); + sprintf( dn, "cn=%s, " BASE, name ); + sprintf( cmpval, "%d", randval + random() % 3 ); + bv.bv_val = cmpval; + bv.bv_len = strlen( cmpval ); + + printf( "Thread %s: Comparing cn in entry (%s) with %s\n", + id, dn, cmpval ); + + rc = ldap_compare_ext_s( ld, dn, "cn", &bv, NULL, NULL ); + switch ( rc ) { + case LDAP_COMPARE_TRUE: + printf( "Thread %s: entry (%s) contains cn %s\n", + id, dn, cmpval ); + break; + case LDAP_COMPARE_FALSE: + printf( "Thread %s: entry (%s) doesn't contain cn %s\n", + id, dn, cmpval ); + break; + default: + ldap_perror( ld, "ldap_compare_ext_s" ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_compare_ext_s" ); + voidrc = (void *)1; + goto compare_cleanup_and_return; + } + } + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +compare_cleanup_and_return: + printf( "Thread %s: attempted %d compare operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +search_thread( char *id ) +{ + LDAPMessage *res, *entry; + char *dn, filter[40]; + int rc, opcount; + void *voidrc = (void *)0; + + printf( "Starting search_thread %s.\n", id ); + opcount = 0; + tsd_setup(); + + for ( ;; ) { + if ( range_filters ) { + switch( get_random_id() % 3 ) { + case 0: + sprintf( filter, "(cn>=%d)", get_random_id()); + break; + case 1: + sprintf( filter, "(cn<=%d)", get_random_id()); + break; + case 2: + sprintf( filter, "(&(cn>=%d)(cn<=%d))", + get_random_id(), get_random_id() ); + break; + } + } else { + sprintf( filter, "cn=%d", get_random_id() ); + } + + printf( "Thread %s: Searching for entry (%s)\n", id, filter ); + + res = NULL; + if (( rc = ldap_search_ext_s( ld, BASE, SCOPE, filter, NULL, 0, + NULL, NULL, NULL, 0, &res )) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_search_ext_s" ); + if ( rc == LDAP_SERVER_DOWN ) { + perror( "ldap_search_ext_s" ); + voidrc = (void *)1; + goto search_cleanup_and_return; + } + } + if ( res != NULL ) { + entry = ldap_first_entry( ld, res ); + if ( entry == NULL ) { + printf( "Thread %s: found no entries\n", id ); + } else { + dn = ldap_get_dn( ld, entry ); + printf( + "Thread %s: found entry (%s); %d total\n", + id, dn == NULL ? "(Null)" : dn, + ldap_count_entries( ld, res )); + ldap_memfree( dn ); + } + ldap_msgfree( res ); + } + + ++opcount; + if ( maxops != 0 && opcount >= maxops ) { + break; + } + } + +search_cleanup_and_return: + printf( "Thread %s: attempted %d search operations\n", id, opcount ); + set_ld_error( 0, NULL, NULL, NULL ); /* disposes of memory */ + tsd_cleanup(); + free( id ); + return voidrc; +} + + +static void * +my_mutex_alloc( void ) +{ + pthread_mutex_t *mutexp; + + if ( (mutexp = malloc( sizeof(pthread_mutex_t) )) != NULL ) { + pthread_mutex_init( mutexp, NULL ); + } + return( mutexp ); +} + + +void * +my_sema_alloc( void ) +{ + sema_t *semptr; + + if( (semptr = malloc( sizeof(sema_t) ) ) != NULL ) { + sema_init( semptr, 0, USYNC_THREAD, NULL ); + } + return ( semptr ); +} + + +static void +my_mutex_free( void *mutexp ) +{ + pthread_mutex_destroy( (pthread_mutex_t *) mutexp ); + free( mutexp ); +} + + +void +my_sema_free( void *semptr ) +{ + sema_destroy( (sema_t *) semptr ); + free( semptr ); +} + + +int +my_sema_wait( void *semptr ) +{ + if( semptr != NULL ) + return( sema_wait( (sema_t *) semptr ) ); + else + return( -1 ); +} + + +int +my_sema_post( void *semptr ) +{ + if( semptr != NULL ) + return( sema_post( (sema_t *) semptr ) ); + else + return( -1 ); +} + + +struct ldap_error { + int le_errno; + char *le_matched; + char *le_errmsg; +}; + + +static void +tsd_setup() +{ + void *tsd; + tsd = pthread_getspecific( key ); + if ( tsd != NULL ) { + fprintf( stderr, "tsd non-null!\n" ); + pthread_exit( NULL ); + } + + tsd = (void *) calloc( 1, sizeof(struct ldap_error) ); + pthread_setspecific( key, tsd ); +} + + +static void +tsd_cleanup() +{ + void *tsd; + + if (( tsd = pthread_getspecific( key )) != NULL ) { + pthread_setspecific( key, NULL ); + free( tsd ); + } +} + + +static void +set_ld_error( int err, char *matched, char *errmsg, void *dummy ) +{ + struct ldap_error *le; + + le = pthread_getspecific( key ); + + le->le_errno = err; + + if ( le->le_matched != NULL ) { + ldap_memfree( le->le_matched ); + } + le->le_matched = matched; + + if ( le->le_errmsg != NULL ) { + ldap_memfree( le->le_errmsg ); + } + le->le_errmsg = errmsg; +} + + +static int +get_ld_error( char **matchedp, char **errmsgp, void *dummy ) +{ + struct ldap_error *le; + + le = pthread_getspecific( key ); + if ( matchedp != NULL ) { + *matchedp = le->le_matched; + } + if ( errmsgp != NULL ) { + *errmsgp = le->le_errmsg; + } + return( le->le_errno ); +} + + +static void +set_errno( int err ) +{ + errno = err; +} + + +static int +get_errno( void ) +{ + return( errno ); +} + + +static int +get_random_id() +{ + return( random() % maxid ); +} + + +static char * +get_id_str( int id ) +{ + char idstr[ 10 ]; + + sprintf( idstr, "%d", id ); + return( strdup( idstr )); +} diff --git a/ldap/c-sdk/libraries/libldap/pwmodext.c b/ldap/c-sdk/libraries/libldap/pwmodext.c new file mode 100644 index 0000000000..fd737c4a3f --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/pwmodext.c @@ -0,0 +1,265 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): abobrov + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_passwd */ +int +LDAP_CALL +ldap_passwd ( + LDAP *ld, + struct berval *userid, + struct berval *oldpasswd, + struct berval *newpasswd, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp + ) +{ + int rc; + BerElement *ber = NULL; + struct berval *requestdata = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + requestdata = NSLDAPI_MALLOC( sizeof( struct berval ) ); + if ( requestdata == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* If the requestValue field is provided, it SHALL contain a + * PasswdModifyRequestValue with one or more fields present. + */ + if ( userid || oldpasswd || newpasswd ) { + if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) + != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* + * PasswdModifyRequestValue ::= SEQUENCE { + * userIdentity [0] OCTET STRING OPTIONAL + * oldPasswd [1] OCTET STRING OPTIONAL + * newPasswd [2] OCTET STRING OPTIONAL } + */ + if ( LBER_ERROR == ( ber_printf( ber, "{" ) ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if ( userid && userid->bv_val && userid->bv_len ) { + if ( LBER_ERROR == ( ber_printf( ber, "to", LDAP_TAG_PWDMOD_REQ_ID, + userid->bv_val, userid->bv_len ) ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + + if ( oldpasswd && oldpasswd->bv_val && oldpasswd->bv_len ) { + if ( LBER_ERROR == ( ber_printf( ber, "to", LDAP_TAG_PWDMOD_REQ_OLD, + oldpasswd->bv_val, oldpasswd->bv_len ) ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + + if ( newpasswd && newpasswd->bv_val && newpasswd->bv_len ) { + if ( LBER_ERROR == ( ber_printf( ber, "to", LDAP_TAG_PWDMOD_REQ_NEW, + newpasswd->bv_val, newpasswd->bv_len ) ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + + if ( LBER_ERROR == ( ber_printf( ber, "}" ) ) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + /* allocate struct berval with contents of the BER encoding */ + rc = ber_flatten( ber, &requestdata ); + if ( rc == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_NO_MEMORY ); + } + } else { + requestdata = NULL; + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_MODIFY_PASSWD, requestdata, + serverctrls, clientctrls, msgidp ); + + /* the ber encoding is no longer needed */ + if ( requestdata ) { + ber_bvfree( requestdata ); + } + + if ( ber ) { + ber_free( ber, 1 ); + } + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + +/* ldap_parse_passwd */ +int +LDAP_CALL +ldap_parse_passwd ( + LDAP *ld, + LDAPMessage *result, + struct berval *genpasswd + ) +{ + int rc; + char *retoidp = NULL; + struct berval *retdatap = NULL; + BerElement *ber = NULL; + ber_len_t len; + ber_tag_t tag; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( !result ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( !genpasswd ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + rc = ldap_parse_extended_result( ld, result, &retoidp, &retdatap, 0 ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_get_lderrno( ld, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + if ( retdatap ) { + /* allocate a Ber element with the contents of the + * berval from the response. + */ + if ( ( ber = ber_init( retdatap ) ) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + if ( ( tag = ber_skip_tag( ber, &len ) ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + ldap_memfree( retoidp ); + return( LDAP_DECODING_ERROR ); + } + + if ( ( ( tag = ber_peek_tag( ber, &len ) ) == LBER_ERROR ) || + tag != LDAP_TAG_PWDMOD_RES_GEN ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + ldap_memfree( retoidp ); + return( LDAP_DECODING_ERROR ); + } + + if ( ber_scanf( ber, "o}", genpasswd ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + ldap_memfree( retoidp ); + return( LDAP_DECODING_ERROR ); + } + + /* the ber encoding is no longer needed */ + ber_free( ber,1 ); + } + + ldap_memfree( retoidp ); + return( LDAP_SUCCESS ); +} + +/* ldap_passwd_s */ +int +LDAP_CALL +ldap_passwd_s ( + LDAP *ld, + struct berval *userid, + struct berval *oldpasswd, + struct berval *newpasswd, + struct berval *genpasswd, + LDAPControl **serverctrls, + LDAPControl **clientctrls + ) +{ + int rc, msgid; + LDAPMessage *result = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + rc = ldap_passwd( ld, userid, oldpasswd, newpasswd, serverctrls, + clientctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ); + if ( rc == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + rc = ldap_parse_passwd( ld, result, genpasswd ); + + ldap_msgfree( result ); + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libldap/pwpctrl.c b/ldap/c-sdk/libraries/libldap/pwpctrl.c new file mode 100644 index 0000000000..26a3f1b2f6 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/pwpctrl.c @@ -0,0 +1,315 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): abobrov@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_create_passwordpolicy_control: + +Parameters are + +ld LDAP pointer to the desired connection + +ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_passwordpolicy_control ( + LDAP *ld, + LDAPControl **ctrlp + ) +{ + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_PASSWD_POLICY, + NULL, 0, 0, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + +/* ldap_create_passwordpolicy_control_ext: + +Parameters are + +ld LDAP pointer to the desired connection + +ctl_iscritical Indicates whether the control is critical of not. If + this field is non-zero, the operation will only be car- + ried out if the control is recognized by the server + and/or client + +ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_passwordpolicy_control_ext ( + LDAP *ld, + const char ctl_iscritical, + LDAPControl **ctrlp + ) +{ + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_PASSWD_POLICY, + NULL, 0, ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + +/* ldap_parse_passwordpolicy_control: + +Parameters are + +ld LDAP pointer to the desired connection + +ctrlp pointer to LDAPControl structure, obtained from + calling ldap_find_control() or by other means. + +exptimep result parameter is filled in with the number of seconds before + the password will expire. + +gracep result parameter is filled in with the number of grace logins + after the password has expired. + +errorcodep result parameter is filled in with the error code of the + password operation. +*/ + +int +LDAP_CALL +ldap_parse_passwordpolicy_control ( + LDAP *ld, + LDAPControl *ctrlp, + ber_int_t *expirep, + ber_int_t *gracep, + LDAPPasswordPolicyError *errorp + ) +{ + ber_len_t len; + ber_tag_t tag; + ber_int_t pp_exp = -1; + ber_int_t pp_grace = -1; + ber_int_t pp_warning = -1; + ber_int_t pp_err = PP_noError; + BerElement *ber = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + + /* allocate a Ber element with the contents of the control's struct berval */ + if ( ( ber = ber_init( &ctrlp->ldctl_value ) ) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* + * The control value should look like this: + * + * PasswordPolicyResponseValue ::= SEQUENCE { + * warning [0] CHOICE { + * timeBeforeExpiration [0] INTEGER (0 .. maxInt), + * graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL + * error [1] ENUMERATED { + * passwordExpired (0), + * accountLocked (1), + * changeAfterReset (2), + * passwordModNotAllowed (3), + * mustSupplyOldPassword (4), + * insufficientPasswordQuality (5), + * passwordTooShort (6), + * passwordTooYoung (7), + * passwordInHistory (8) } OPTIONAL } + */ + + if ( ber_scanf( ber, "{" ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + + tag = ber_peek_tag( ber, &len ); + + while ( (tag != LBER_ERROR) && (tag != LBER_END_OF_SEQORSET) ) { + if ( tag == ( LBER_CONSTRUCTED | LBER_CLASS_CONTEXT ) ) { + ber_skip_tag( ber, &len ); + if ( ber_scanf( ber, "ti", &tag, &pp_warning ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + if ( tag == LBER_CLASS_CONTEXT ) { + pp_exp = pp_warning; + } else if ( tag == ( LBER_CLASS_CONTEXT | 0x01 ) ) { + pp_grace = pp_warning; + } + } else if ( tag == ( LBER_CLASS_CONTEXT | 0x01 ) ) { + if ( ber_scanf( ber, "ti", &tag, &pp_err ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + } + if ( tag == LBER_DEFAULT ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + tag = ber_skip_tag( ber, &len ); + } + + if (expirep) *expirep = pp_exp; + if (gracep) *gracep = pp_grace; + if (errorp) *errorp = pp_err; + + /* the ber encoding is no longer needed */ + ber_free( ber, 1 ); + return( LDAP_SUCCESS ); +} + +/* ldap_parse_passwordpolicy_control_ext: + +Parameters are + +ld LDAP pointer to the desired connection + +ctrlp An array of controls obtained from calling + ldap_parse_result on the set of results + returned by the server + +exptimep result parameter is filled in with the number of seconds before + the password will expire. + +gracep result parameter is filled in with the number of grace logins + after the password has expired. + +errorcodep result parameter is filled in with the error code of the + password operation. +*/ + +int +LDAP_CALL +ldap_parse_passwordpolicy_control_ext ( + LDAP *ld, + LDAPControl **ctrlp, + ber_int_t *expirep, + ber_int_t *gracep, + LDAPPasswordPolicyError *errorp + ) +{ + int i, foundPPControl; + LDAPControl *PPCtrlp = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) { + return( LDAP_PARAM_ERROR ); + } + + /* find the control in the list of controls if it exists */ + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + foundPPControl = 0; + for ( i = 0; (( ctrlp[i] != NULL ) && ( !foundPPControl )); i++ ) { + foundPPControl = !strcmp( ctrlp[i]->ldctl_oid, LDAP_CONTROL_PASSWD_POLICY ); + } + if ( !foundPPControl ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } else { + /* let local var point to the control */ + PPCtrlp = ctrlp[i-1]; + } + + return ( + ldap_parse_passwordpolicy_control( ld, PPCtrlp, expirep, gracep, errorp )); +} + +const char * +LDAP_CALL +ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err ) +{ + switch(err) { + case PP_passwordExpired: + return "Password expired"; + case PP_accountLocked: + return "Account locked"; + case PP_changeAfterReset: + return "Password must be changed"; + case PP_passwordModNotAllowed: + return "Policy prevents password modification"; + case PP_mustSupplyOldPassword: + return "Policy requires old password in order to change password"; + case PP_insufficientPasswordQuality: + return "Password fails quality checks"; + case PP_passwordTooShort: + return "Password is too short for policy"; + case PP_passwordTooYoung: + return "Password has been changed too recently"; + case PP_passwordInHistory: + return "New password is in list of old passwords"; + case PP_noError: + return "No error"; + default: + return "Unknown error code"; + } +} diff --git a/ldap/c-sdk/libraries/libldap/referral.c b/ldap/c-sdk/libraries/libldap/referral.c new file mode 100644 index 0000000000..742d3d9861 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/referral.c @@ -0,0 +1,177 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * referral.c - routines for handling LDAPv3 referrals and references. + */ + +#include "ldap-int.h" + + +LDAPMessage * +LDAP_CALL +ldap_first_reference( LDAP *ld, LDAPMessage *res ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || res == NULLMSG ) { + return( NULLMSG ); + } + + if ( res->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + return( res ); + } + + return( ldap_next_reference( ld, res )); +} + + +LDAPMessage * +LDAP_CALL +ldap_next_reference( LDAP *ld, LDAPMessage *ref ) +{ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || ref == NULLMSG ) { + return( NULLMSG ); /* punt */ + } + + for ( ref = ref->lm_chain; ref != NULLMSG; ref = ref->lm_chain ) { + if ( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + return( ref ); + } + } + + return( NULLMSG ); +} + + +int +LDAP_CALL +ldap_count_references( LDAP *ld, LDAPMessage *res ) +{ + int i; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); + } + + for ( i = 0; res != NULL; res = res->lm_chain ) { + if ( res->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + ++i; + } + } + + return( i ); +} + + +/* + * returns an LDAP error code. + */ +int +LDAP_CALL +ldap_parse_reference( LDAP *ld, LDAPMessage *ref, char ***referralsp, + LDAPControl ***serverctrlsp, int freeit ) +{ + int err; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || + !NSLDAPI_VALID_LDAPMESSAGE_REFERENCE_POINTER( ref )) { + return( LDAP_PARAM_ERROR ); + } + + err = nsldapi_parse_reference( ld, ref->lm_ber, referralsp, + serverctrlsp ); + + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + + if ( freeit ) { + ldap_msgfree( ref ); + } + + return( err ); +} + + +/* + * returns an LDAP error code indicating success or failure of parsing + * does NOT set any error information inside "ld" + */ +int +nsldapi_parse_reference( LDAP *ld, BerElement *rber, char ***referralsp, + LDAPControl ***serverctrlsp ) +{ + int err; + BerElement ber; + char **refs; + + /* + * Parse a searchResultReference message. These are used in LDAPv3 + * and beyond and look like this: + * + * SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL + * + * all wrapped up in an LDAPMessage sequence which looks like this: + * + * LDAPMessage ::= SEQUENCE { + * messageID MessageID, + * SearchResultReference + * controls [0] Controls OPTIONAL + * } + * + * ldap_result() pulls out the message id, so by the time a result + * message gets here we are conveniently sitting at the start of the + * SearchResultReference itself. + */ + err = LDAP_SUCCESS; /* optimistic */ + ber = *rber; /* struct copy */ + + if ( ber_scanf( &ber, "{v", &refs ) == LBER_ERROR ) { + err = LDAP_DECODING_ERROR; + } else if ( serverctrlsp != NULL ) { + /* pull out controls (if requested and any are present) */ + if ( ber_scanf( &ber, "}" ) == LBER_ERROR ) { + err = LDAP_DECODING_ERROR; + } else { + err = nsldapi_get_controls( &ber, serverctrlsp ); + } + } + + if ( referralsp == NULL ) { + ldap_value_free( refs ); + } else { + *referralsp = refs; + } + + return( err ); +} diff --git a/ldap/c-sdk/libraries/libldap/regex.c b/ldap/c-sdk/libraries/libldap/regex.c new file mode 100644 index 0000000000..4ba4ee08a1 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/regex.c @@ -0,0 +1,920 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" +#if defined( macintosh ) || defined( DOS ) || defined( _WINDOWS ) || defined( NEED_BSDREGEX ) || defined( XP_OS2) +#include "regex.h" + +/* + * regex - Regular expression pattern matching and replacement + * + * By: Ozan S. Yigit (oz) + * Dept. of Computer Science + * York University + * + * These routines are the PUBLIC DOMAIN equivalents of regex + * routines as found in 4.nBSD UN*X, with minor extensions. + * + * These routines are derived from various implementations found + * in software tools books, and Conroy's grep. They are NOT derived + * from licensed/restricted software. + * For more interesting/academic/complicated implementations, + * see Henry Spencer's regexp routines, or GNU Emacs pattern + * matching module. + * + * Use the actual CCL code in the CLO + * section of pmatch. No need for a recursive + * pmatch call. + * + * Use a bitmap table to set char bits in an + * 8-bit chunk. + * + * Interfaces: + * re_comp: compile a regular expression into a NFA. + * + * char *re_comp(s) + * char *s; + * + * re_exec: execute the NFA to match a pattern. + * + * int re_exec(s) + * char *s; + * + * re_modw change re_exec's understanding of what a "word" + * looks like (for \< and \>) by adding into the + * hidden word-syntax table. + * + * void re_modw(s) + * char *s; + * + * re_subs: substitute the matched portions in a new string. + * + * int re_subs(src, dst) + * char *src; + * char *dst; + * + * re_fail: failure routine for re_exec. + * + * void re_fail(msg, op) + * char *msg; + * char op; + * + * Regular Expressions: + * + * [1] char matches itself, unless it is a special + * character (metachar): . \ [ ] * + ^ $ + * + * [2] . matches any character. + * + * [3] \ matches the character following it, except + * when followed by a left or right round bracket, + * a digit 1 to 9 or a left or right angle bracket. + * (see [7], [8] and [9]) + * It is used as an escape character for all + * other meta-characters, and itself. When used + * in a set ([4]), it is treated as an ordinary + * character. + * + * [4] [set] matches one of the characters in the set. + * If the first character in the set is "^", + * it matches a character NOT in the set, i.e. + * complements the set. A shorthand S-E is + * used to specify a set of characters S upto + * E, inclusive. The special characters "]" and + * "-" have no special meaning if they appear + * as the first chars in the set. + * examples: match: + * + * [a-z] any lowercase alpha + * + * [^]-] any char except ] and - + * + * [^A-Z] any char except uppercase + * alpha + * + * [a-zA-Z] any alpha + * + * [5] * any regular expression form [1] to [4], followed by + * closure char (*) matches zero or more matches of + * that form. + * + * [6] + same as [5], except it matches one or more. + * + * [7] a regular expression in the form [1] to [10], enclosed + * as \(form\) matches what form matches. The enclosure + * creates a set of tags, used for [8] and for + * pattern substution. The tagged forms are numbered + * starting from 1. + * + * [8] a \ followed by a digit 1 to 9 matches whatever a + * previously tagged regular expression ([7]) matched. + * + * [9] \< a regular expression starting with a \< construct + * \> and/or ending with a \> construct, restricts the + * pattern matching to the beginning of a word, and/or + * the end of a word. A word is defined to be a character + * string beginning and/or ending with the characters + * A-Z a-z 0-9 and _. It must also be preceded and/or + * followed by any character outside those mentioned. + * + * [10] a composite regular expression xy where x and y + * are in the form [1] to [10] matches the longest + * match of x followed by a match for y. + * + * [11] ^ a regular expression starting with a ^ character + * $ and/or ending with a $ character, restricts the + * pattern matching to the beginning of the line, + * or the end of line. [anchors] Elsewhere in the + * pattern, ^ and $ are treated as ordinary characters. + * + * + * Acknowledgements: + * + * HCR's Hugh Redelmeier has been most helpful in various + * stages of development. He convinced me to include BOW + * and EOW constructs, originally invented by Rob Pike at + * the University of Toronto. + * + * References: + * Software tools Kernighan & Plauger + * Software tools in Pascal Kernighan & Plauger + * Grep [rsx-11 C dist] David Conroy + * ed - text editor Un*x Programmer's Manual + * Advanced editing on Un*x B. W. Kernighan + * RegExp routines Henry Spencer + * + * Notes: + * + * This implementation uses a bit-set representation for character + * classes for speed and compactness. Each character is represented + * by one bit in a 128-bit block. Thus, CCL always takes a + * constant 16 bytes in the internal nfa, and re_exec does a single + * bit comparison to locate the character in the set. + * + * Examples: + * + * pattern: foo*.* + * compile: CHR f CHR o CLO CHR o END CLO ANY END END + * matches: fo foo fooo foobar fobar foxx ... + * + * pattern: fo[ob]a[rz] + * compile: CHR f CHR o CCL bitset CHR a CCL bitset END + * matches: fobar fooar fobaz fooaz + * + * pattern: foo\\+ + * compile: CHR f CHR o CHR o CHR \ CLO CHR \ END END + * matches: foo\ foo\\ foo\\\ ... + * + * pattern: \(foo\)[1-3]\1 (same as foo[1-3]foo) + * compile: BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END + * matches: foo1foo foo2foo foo3foo + * + * pattern: \(fo.*\)-\1 + * compile: BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END + * matches: foo-foo fo-fo fob-fob foobar-foobar ... + */ + +#define MAXNFA 1024 +#define MAXTAG 10 + +#define OKP 1 +#define NOP 0 + +#define CHR 1 +#define ANY 2 +#define CCL 3 +#define BOL 4 +#define EOL 5 +#define BOT 6 +#define EOT 7 +#define BOW 8 +#define EOW 9 +#define REF 10 +#define CLO 11 + +#define END 0 + +/* + * The following defines are not meant to be changeable. + * They are for readability only. + */ +#define MAXCHR 128 +#define CHRBIT 8 +#define BITBLK MAXCHR/CHRBIT +#define BLKIND 0170 +#define BITIND 07 + +#define ASCIIB 0177 + +/* Plain char, on the other hand, may be signed or unsigned; it depends on + * the platform and perhaps a compiler option. A hard fact of life, in C. + * + * 6-April-1999 mcs@netscape.com: replaced CHAR with REGEXCHAR to avoid + * conflicts with system types on Win32. Changed typedef + * for REGEXCHAR to always be unsigned, which seems right. + */ +typedef unsigned char REGEXCHAR; + +static int tagstk[MAXTAG]; /* subpat tag stack..*/ +static REGEXCHAR nfa[MAXNFA]; /* automaton.. */ +static int sta = NOP; /* status of lastpat */ + +static REGEXCHAR bittab[BITBLK]; /* bit table for CCL */ + /* pre-set bits... */ +static REGEXCHAR bitarr[] = {1,2,4,8,16,32,64,128}; + +static void +chset(REGEXCHAR c) +{ + bittab[((c) & (unsigned)BLKIND) >> 3] |= bitarr[(c) & BITIND]; +} + +#define badpat(x) (*nfa = END, x) +#define store(x) *mp++ = x + +char * +LDAP_CALL +re_comp( const char *pat ) +{ + register REGEXCHAR *p; /* pattern pointer */ + register REGEXCHAR *mp=nfa; /* nfa pointer */ + register REGEXCHAR *lp; /* saved pointer.. */ + register REGEXCHAR *sp=nfa; /* another one.. */ + + register int tagi = 0; /* tag stack index */ + register int tagc = 1; /* actual tag count */ + + register int n; + register REGEXCHAR mask; /* xor mask -CCL/NCL */ + int c1, c2; + + if (!pat || !*pat) { + if (sta) { + return 0; + } else { + return badpat("No previous regular expression"); + } + } + sta = NOP; + + for (p = (REGEXCHAR*)pat; *p; p++) { + lp = mp; + switch(*p) { + + case '.': /* match any char.. */ + store(ANY); + break; + + case '^': /* match beginning.. */ + if (p == (REGEXCHAR*)pat) + store(BOL); + else { + store(CHR); + store(*p); + } + break; + + case '$': /* match endofline.. */ + if (!*(p+1)) + store(EOL); + else { + store(CHR); + store(*p); + } + break; + + case '[': /* match char class..*/ + store(CCL); + + if (*++p == '^') { + mask = 0377; + p++; + } + else + mask = 0; + + if (*p == '-') /* real dash */ + chset(*p++); + if (*p == ']') /* real brac */ + chset(*p++); + while (*p && *p != ']') { + if (*p == '-' && *(p+1) && *(p+1) != ']') { + p++; + c1 = *(p-2) + 1; + c2 = *p++; + while (c1 <= c2) + chset((REGEXCHAR)c1++); + } +#ifdef EXTEND + else if (*p == '\\' && *(p+1)) { + p++; + chset(*p++); + } +#endif + else + chset(*p++); + } + if (!*p) + return badpat("Missing ]"); + + for (n = 0; n < BITBLK; bittab[n++] = (REGEXCHAR) 0) + store(mask ^ bittab[n]); + + break; + + case '*': /* match 0 or more.. */ + case '+': /* match 1 or more.. */ + if (p == (REGEXCHAR*)pat) + return badpat("Empty closure"); + lp = sp; /* previous opcode */ + if (*lp == CLO) /* equivalence.. */ + break; + switch(*lp) { + + case BOL: + case BOT: + case EOT: + case BOW: + case EOW: + case REF: + return badpat("Illegal closure"); + default: + break; + } + + if (*p == '+') + for (sp = mp; lp < sp; lp++) + store(*lp); + + store(END); + store(END); + sp = mp; + while (--mp > lp) + *mp = mp[-1]; + store(CLO); + mp = sp; + break; + + case '\\': /* tags, backrefs .. */ + switch(*++p) { + + case '(': + if (tagc < MAXTAG) { + tagstk[++tagi] = tagc; + store(BOT); + store(tagc++); + } + else + return badpat("Too many \\(\\) pairs"); + break; + case ')': + if (*sp == BOT) + return badpat("Null pattern inside \\(\\)"); + if (tagi > 0) { + store(EOT); + store(tagstk[tagi--]); + } + else + return badpat("Unmatched \\)"); + break; + case '<': + store(BOW); + break; + case '>': + if (*sp == BOW) + return badpat("Null pattern inside \\<\\>"); + store(EOW); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = *p-'0'; + if (tagi > 0 && tagstk[tagi] == n) + return badpat("Cyclical reference"); + if (tagc > n) { + store(REF); + store(n); + } + else + return badpat("Undetermined reference"); + break; +#ifdef EXTEND + case 'b': + store(CHR); + store('\b'); + break; + case 'n': + store(CHR); + store('\n'); + break; + case 'f': + store(CHR); + store('\f'); + break; + case 'r': + store(CHR); + store('\r'); + break; + case 't': + store(CHR); + store('\t'); + break; +#endif + default: + store(CHR); + store(*p); + } + break; + + default : /* an ordinary char */ + store(CHR); + store(*p); + break; + } + sp = lp; + } + if (tagi > 0) + return badpat("Unmatched \\("); + store(END); + sta = OKP; + return 0; +} + + +static REGEXCHAR *bol; +static REGEXCHAR *bopat[MAXTAG]; +static REGEXCHAR *eopat[MAXTAG]; +#ifdef NEEDPROTOS +static REGEXCHAR *pmatch( REGEXCHAR *lp, REGEXCHAR *ap ); +#else /* NEEDPROTOS */ +static REGEXCHAR *pmatch(); +#endif /* NEEDPROTOS */ + +/* + * re_exec: + * execute nfa to find a match. + * + * special cases: (nfa[0]) + * BOL + * Match only once, starting from the + * beginning. + * CHR + * First locate the character without + * calling pmatch, and if found, call + * pmatch for the remaining string. + * END + * re_comp failed, poor luser did not + * check for it. Fail fast. + * + * If a match is found, bopat[0] and eopat[0] are set + * to the beginning and the end of the matched fragment, + * respectively. + * + */ + +int +LDAP_CALL +re_exec( const char *lp ) +{ + register REGEXCHAR c; + register REGEXCHAR *ep = 0; + register REGEXCHAR *ap = nfa; + + bol = (REGEXCHAR*)lp; + + bopat[0] = 0; + bopat[1] = 0; + bopat[2] = 0; + bopat[3] = 0; + bopat[4] = 0; + bopat[5] = 0; + bopat[6] = 0; + bopat[7] = 0; + bopat[8] = 0; + bopat[9] = 0; + + switch(*ap) { + + case BOL: /* anchored: match from BOL only */ + ep = pmatch((REGEXCHAR*)lp,ap); + break; + case CHR: /* ordinary char: locate it fast */ + c = *(ap+1); + while (*lp && *(REGEXCHAR*)lp != c) + lp++; + if (!*lp) /* if EOS, fail, else fall thru. */ + return 0; + default: /* regular matching all the way. */ + do { + if ((ep = pmatch((REGEXCHAR*)lp,ap))) + break; + lp++; + } while (*lp); + + break; + case END: /* munged automaton. fail always */ + return 0; + } + if (!ep) + return 0; + + bopat[0] = (REGEXCHAR*)lp; + eopat[0] = ep; + return 1; +} + +/* + * pmatch: internal routine for the hard part + * + * This code is partly snarfed from an early grep written by + * David Conroy. The backref and tag stuff, and various other + * innovations are by oz. + * + * special case optimizations: (nfa[n], nfa[n+1]) + * CLO ANY + * We KNOW .* will match everything upto the + * end of line. Thus, directly go to the end of + * line, without recursive pmatch calls. As in + * the other closure cases, the remaining pattern + * must be matched by moving backwards on the + * string recursively, to find a match for xy + * (x is ".*" and y is the remaining pattern) + * where the match satisfies the LONGEST match for + * x followed by a match for y. + * CLO CHR + * We can again scan the string forward for the + * single char and at the point of failure, we + * execute the remaining nfa recursively, same as + * above. + * + * At the end of a successful match, bopat[n] and eopat[n] + * are set to the beginning and end of subpatterns matched + * by tagged expressions (n = 1 to 9). + * + */ + +#ifndef re_fail +extern void re_fail(); +#endif /* re_fail */ + +/* + * character classification table for word boundary operators BOW + * and EOW. the reason for not using ctype macros is that we can + * let the user add into our own table. see re_modw. This table + * is not in the bitset form, since we may wish to extend it in the + * future for other character classifications. + * + * TRUE for 0-9 A-Z a-z _ + */ +static char chrtyp[MAXCHR] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0 + }; + +#define HIBIT 0200 +#define inascii(x) (0177&(x)) +#define iswordc(x) chrtyp[inascii(x)] +#define isinset(x,y) (((y)&HIBIT)?0:((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])) + +/* + * skip values for CLO XXX to skip past the closure + */ + +#define ANYSKIP 2 /* [CLO] ANY END ... */ +#define CHRSKIP 3 /* [CLO] CHR chr END ... */ +#define CCLSKIP 18 /* [CLO] CCL 16bytes END ... */ + +static REGEXCHAR * +pmatch( REGEXCHAR *lp, REGEXCHAR *ap) +{ + register int op, c, n; + register REGEXCHAR *e; /* extra pointer for CLO */ + register REGEXCHAR *bp; /* beginning of subpat.. */ + register REGEXCHAR *ep; /* ending of subpat.. */ + REGEXCHAR *are; /* to save the line ptr. */ + + while ((op = *ap++) != END) + switch(op) { + + case CHR: + if (*lp++ != *ap++) + return 0; + break; + case ANY: + if (!*lp++) + return 0; + break; + case CCL: + c = *lp++; + if (!isinset(ap,c)) + return 0; + ap += BITBLK; + break; + case BOL: + if (lp != bol) + return 0; + break; + case EOL: + if (*lp) + return 0; + break; + case BOT: + bopat[*ap++] = lp; + break; + case EOT: + eopat[*ap++] = lp; + break; + case BOW: + if ((lp!=bol && iswordc(lp[-1])) || !iswordc(*lp)) + return 0; + break; + case EOW: + if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp)) + return 0; + break; + case REF: + n = *ap++; + bp = bopat[n]; + ep = eopat[n]; + while (bp < ep) + if (*bp++ != *lp++) + return 0; + break; + case CLO: + are = lp; + switch(*ap) { + + case ANY: + while (*lp) + lp++; + n = ANYSKIP; + break; + case CHR: + c = *(ap+1); + while (*lp && c == *lp) + lp++; + n = CHRSKIP; + break; + case CCL: + while ((c = *lp) && isinset(ap+1,c)) + lp++; + n = CCLSKIP; + break; + default: + re_fail("closure: bad nfa.", *ap); + return 0; + } + + ap += n; + + while (lp >= are) { + if ((e = pmatch(lp, ap))) + return e; + --lp; + } + return 0; + default: + re_fail("re_exec: bad nfa.", op); + return 0; + } + return lp; +} + +/* + * re_modw: + * add new characters into the word table to change re_exec's + * understanding of what a word should look like. Note that we + * only accept additions into the word definition. + * + * If the string parameter is 0 or null string, the table is + * reset back to the default containing A-Z a-z 0-9 _. [We use + * the compact bitset representation for the default table] + */ + +static REGEXCHAR deftab[16] = { + 0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207, + 0376, 0377, 0377, 007 +}; + +void +LDAP_CALL +re_modw( char *s ) +{ + register int i; + + if (!s || !*s) { + for (i = 0; i < MAXCHR; i++) + if (!isinset(deftab,i)) + iswordc(i) = 0; + } + else + while(*s) + iswordc(*s++) = 1; +} + +/* + * re_subs: + * substitute the matched portions of the src in dst. + * + * & substitute the entire matched pattern. + * + * \digit substitute a subpattern, with the given tag number. + * Tags are numbered from 1 to 9. If the particular + * tagged subpattern does not exist, null is substituted. + */ +int +LDAP_CALL +re_subs( char *src, char *dst) +{ + register char c; + register int pin; + register REGEXCHAR *bp; + register REGEXCHAR *ep; + + if (!*src || !bopat[0]) + return 0; + + while ((c = *src++)) { + switch(c) { + + case '&': + pin = 0; + break; + + case '\\': + c = *src++; + if (c >= '0' && c <= '9') { + pin = c - '0'; + break; + } + + default: + *dst++ = c; + continue; + } + + if ((bp = bopat[pin]) && (ep = eopat[pin])) { + while (*bp && bp < ep) + *dst++ = *(char*)bp++; + if (bp < ep) + return 0; + } + } + *dst = (char) 0; + return 1; +} + +#ifdef DEBUG + +/* No printf or exit in 16-bit Windows */ +#if defined( _WINDOWS ) && !defined( _WIN32 ) +static int LDAP_C printf( const char* pszFormat, ...) +{ + char buf[1024]; + va_list arglist; + va_start(arglist, pszFormat); + vsprintf(buf, pszFormat, arglist); + va_end(arglist); + OutputDebugString(buf); + return 0; +} +#define exit(v) return +#endif /* 16-bit Windows */ + + +#ifdef REGEX_DEBUG + +static void nfadump( REGEXCHAR *ap); + +/* + * symbolic - produce a symbolic dump of the nfa + */ +void +symbolic( char *s ) +{ + printf("pattern: %s\n", s); + printf("nfacode:\n"); + nfadump(nfa); +} + +static void +nfadump( REGEXCHAR *ap) +{ + register int n; + + while (*ap != END) + switch(*ap++) { + case CLO: + printf("CLOSURE"); + nfadump(ap); + switch(*ap) { + case CHR: + n = CHRSKIP; + break; + case ANY: + n = ANYSKIP; + break; + case CCL: + n = CCLSKIP; + break; + } + ap += n; + break; + case CHR: + printf("\tCHR %c\n",*ap++); + break; + case ANY: + printf("\tANY .\n"); + break; + case BOL: + printf("\tBOL -\n"); + break; + case EOL: + printf("\tEOL -\n"); + break; + case BOT: + printf("BOT: %d\n",*ap++); + break; + case EOT: + printf("EOT: %d\n",*ap++); + break; + case BOW: + printf("BOW\n"); + break; + case EOW: + printf("EOW\n"); + break; + case REF: + printf("REF: %d\n",*ap++); + break; + case CCL: + printf("\tCCL ["); + for (n = 0; n < MAXCHR; n++) + if (isinset(ap,(REGEXCHAR)n)) { + if (n < ' ') + printf("^%c", n ^ 0x040); + else + printf("%c", n); + } + printf("]\n"); + ap += BITBLK; + break; + default: + printf("bad nfa. opcode %o\n", ap[-1]); + exit(1); + break; + } +} +#endif /* REGEX_DEBUG */ +#endif /* DEBUG */ +#endif /* macintosh or DOS or _WINDOWS or NEED_BSDREGEX */ diff --git a/ldap/c-sdk/libraries/libldap/rename.c b/ldap/c-sdk/libraries/libldap/rename.c new file mode 100644 index 0000000000..ac3ec7c737 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/rename.c @@ -0,0 +1,265 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * rename.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * ldap_rename - initiate an ldap modifyDN operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN that will form leftmost component of entry's new name + * newparent if present, this is the Distinguished Name of the entry + * which becomes the immediate parent of the existing entry + * deleteoldrdn nonzero means to delete old rdn values from the entry + * while zero means to retain them as attributes of the entry + * serverctrls list of LDAP server controls + * clientctrls list of client controls + * msgidp this result parameter will be set to the message id of the + * request if the ldap_rename() call succeeds + * + * Example: + * int rc; + * rc = ldap_rename( ld, dn, newrdn, newparent, deleteoldrdn, serverctrls, clientctrls, &msgid ); + */ +int +LDAP_CALL +ldap_rename( + LDAP *ld, + const char *dn, + const char *newrdn, + const char *newparent, + int deleteoldrdn, + LDAPControl **serverctrls, + LDAPControl **clientctrls, /* not used for anything yet */ + int *msgidp +) +{ + BerElement *ber; + int rc, err; + + /* + * A modify dn request looks like this: + * ModifyDNRequest ::= SEQUENCE { + * entry LDAPDN, + * newrdn RelativeLDAPDN, + * newparent [0] LDAPDN OPTIONAL, + * deleteoldrdn BOOLEAN + * } + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_rename\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + if ( NULL == newrdn) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + /* only ldapv3 or higher can do a proper rename + * (i.e. with non-NULL newparent and/or controls) + */ + + if (( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) + && ((newparent != NULL) || (serverctrls != NULL) + || (clientctrls != NULL))) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + if ( msgidp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* see if modRDN or modDN is handled by the cache */ + if ( ld->ld_cache_on ) { + if ( newparent == NULL && ld->ld_cache_modrdn != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_modrdn)( ld, *msgidp, + LDAP_REQ_MODRDN, dn, newrdn, deleteoldrdn )) + != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); +#if 0 + } else if ( ld->ld_cache_rename != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_rename)( ld, *msgidp, + LDAP_REQ_MODDN, dn, newrdn, newparent, + deleteoldrdn )) != 0 ) { + *msgidp = rc; + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); +#endif + } + } + + /* create a message to send */ + if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( err ); + } + + /* fill it in */ + if ( ber_printf( ber, "{it{ssb", *msgidp, LDAP_REQ_MODDN, dn, + newrdn, deleteoldrdn ) == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if ( newparent == NULL ) { + if ( ber_printf( ber, "}" ) == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } else { + if ( ber_printf( ber, "ts}", LDAP_TAG_NEWSUPERIOR, newparent ) + == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + + if (( rc = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( rc ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_MODDN, + (char *) dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +int +LDAP_CALL +ldap_modrdn2( LDAP *ld, const char *dn, const char *newrdn, int deleteoldrdn ) +{ + int msgid; + + if ( ldap_rename( ld, dn, newrdn, NULL, deleteoldrdn, NULL, NULL, &msgid ) == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + +int +LDAP_CALL +ldap_modrdn( LDAP *ld, const char *dn, const char *newrdn ) +{ + return( ldap_modrdn2( ld, dn, newrdn, 1 ) ); +} + +int +LDAP_CALL +ldap_rename_s( + LDAP *ld, + const char *dn, + const char *newrdn, + const char *newparent, + int deleteoldrdn, + LDAPControl **serverctrls, + LDAPControl **clientctrls /* not used for anything yet */ +) +{ + int msgid; + LDAPMessage *res; + + if ( ldap_rename( ld, dn, newrdn, newparent, deleteoldrdn, serverctrls, clientctrls, &msgid ) != LDAP_SUCCESS ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( msgid == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, &res ) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +LDAP_CALL +ldap_modrdn2_s( LDAP *ld, const char *dn, const char *newrdn, int deleteoldrdn ) +{ + int msgid; + LDAPMessage *res; + + if ( (msgid = ldap_modrdn2( ld, dn, newrdn, deleteoldrdn )) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + if ( ldap_result( ld, msgid, 1, (struct timeval *) NULL, &res ) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +LDAP_CALL +ldap_modrdn_s( LDAP *ld, const char *dn, const char *newrdn ) +{ + return( ldap_modrdn2_s( ld, dn, newrdn, 1 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/request.c b/ldap/c-sdk/libraries/libldap/request.c new file mode 100644 index 0000000000..d379917b2b --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/request.c @@ -0,0 +1,1659 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * request.c - sending of ldap requests; handling of referrals + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +static LDAPConn *find_connection( LDAP *ld, LDAPServer *srv, int any ); +static void free_servers( LDAPServer *srvlist ); +static int chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq, + char *refurl, char *desc, int *unknownp, int is_reference ); +static int re_encode_request( LDAP *ld, BerElement *origber, + int msgid, LDAPURLDesc *ludp, BerElement **berp, int is_reference ); + +#ifdef LDAP_DNS +static LDAPServer *dn2servers( LDAP *ld, char *dn ); +#endif /* LDAP_DNS */ + + +/* returns an LDAP error code and also sets error inside LDAP * */ +int +nsldapi_alloc_ber_with_options( LDAP *ld, BerElement **berp ) +{ + int err; + + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + if (( *berp = ber_alloc_t( ld->ld_lberoptions )) == NULLBER ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + } else { + err = LDAP_SUCCESS; +#ifdef STR_TRANSLATION + nsldapi_set_ber_options( ld, *berp ); +#endif /* STR_TRANSLATION */ + } + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + + return( err ); +} + + +void +nsldapi_set_ber_options( LDAP *ld, BerElement *ber ) +{ + ber->ber_options = ld->ld_lberoptions; +#ifdef STR_TRANSLATION + if (( ld->ld_lberoptions & LBER_OPT_TRANSLATE_STRINGS ) != 0 ) { + ber_set_string_translators( ber, + ld->ld_lber_encode_translate_proc, + ld->ld_lber_decode_translate_proc ); + } +#endif /* STR_TRANSLATION */ +} + + +/* returns the message id of the request or -1 if an error occurs */ +int +nsldapi_send_initial_request( LDAP *ld, int msgid, unsigned long msgtype, + char *dn, BerElement *ber ) +{ + LDAPServer *servers; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_initial_request\n", 0,0,0 ); + +#ifdef LDAP_DNS + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + if (( ld->ld_options & LDAP_BITOPT_DNS ) != 0 && ldap_is_dns_dn( dn )) { + if (( servers = dn2servers( ld, dn )) == NULL ) { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + return( -1 ); + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + LDAPServer *srv; + char msg[256]; + + for ( srv = servers; srv != NULL; + srv = srv->lsrv_next ) { + sprintf( msg, + "LDAP server %s: dn %s, port %d\n", + srv->lsrv_host, ( srv->lsrv_dn == NULL ) ? + "(default)" : srv->lsrv_dn, + srv->lsrv_port ); + ber_err_print( msg ); + } + } +#endif /* LDAP_DEBUG */ + } else { +#endif /* LDAP_DNS */ + /* + * use of DNS is turned off or this is an LDAP DN... + * use our default connection + */ + servers = NULL; +#ifdef LDAP_DNS + } + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); +#endif /* LDAP_DNS */ + + return( nsldapi_send_server_request( ld, ber, msgid, NULL, + servers, NULL, ( msgtype == LDAP_REQ_BIND ) ? dn : NULL, 0 )); +} + + +/* returns the message id of the request or -1 if an error occurs */ +int +nsldapi_send_server_request( + LDAP *ld, /* session handle */ + BerElement *ber, /* message to send */ + int msgid, /* ID of message to send */ + LDAPRequest *parentreq, /* non-NULL for referred requests */ + LDAPServer *srvlist, /* servers to connect to (NULL for default) */ + LDAPConn *lc, /* connection to use (NULL for default) */ + char *bindreqdn, /* non-NULL for bind requests */ + int bind /* perform a bind after opening new conn.? */ +) +{ + LDAPRequest *lr; + int err; + int incparent; /* did we bump parent's ref count? */ + /* EPIPE and Unsolicited Response handling variables */ + int res_rc = 0; + int epipe_err = 0; + int ext_res_rc = 0; + char *ext_oid = NULL; + struct berval *ext_data = NULL; + LDAPMessage *ext_res = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_server_request\n", 0, 0, 0 ); + + incparent = 0; + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + if ( lc == NULL ) { + if ( srvlist == NULL ) { + if ( ld->ld_defconn == NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + if ( bindreqdn == NULL && ( ld->ld_options + & LDAP_BITOPT_RECONNECT ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, + NULL, NULL ); + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + + if ( nsldapi_open_ldap_defconn( ld ) < 0 ) { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + } + lc = ld->ld_defconn; + } else { + if (( lc = find_connection( ld, srvlist, 1 )) == + NULL ) { + if ( bind && (parentreq != NULL) ) { + /* Remember the bind in the parent */ + incparent = 1; + ++parentreq->lr_outrefcnt; + } + + lc = nsldapi_new_connection( ld, &srvlist, 0, + 1, bind ); + } + free_servers( srvlist ); + } + } + + + /* + * return a fatal error if: + * 1. no connections exists + * or + * 2. the connection is dead + * or + * 3. it is not in the connected state with normal (non async) I/O + */ + if ( lc == NULL + || ( lc->lconn_status == LDAP_CONNST_DEAD ) + || ( 0 == (ld->ld_options & LDAP_BITOPT_ASYNC) && + lc->lconn_status != LDAP_CONNST_CONNECTED) ) { + + ber_free( ber, 1 ); + if ( lc != NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); + } + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + + if (( lr = nsldapi_new_request( lc, ber, msgid, + 1 /* expect a response */)) == NULL + || ( bindreqdn != NULL && ( bindreqdn = + nsldapi_strdup( bindreqdn )) == NULL )) { + if ( lr != NULL ) { + NSLDAPI_FREE( lr ); + } + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 ); + ber_free( ber, 1 ); + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + lr->lr_binddn = bindreqdn; + + if ( parentreq != NULL ) { /* sub-request */ + if ( !incparent ) { + /* Increment if we didn't do it before the bind */ + ++parentreq->lr_outrefcnt; + } + lr->lr_origid = parentreq->lr_origid; + lr->lr_parentcnt = parentreq->lr_parentcnt + 1; + lr->lr_parent = parentreq; + if ( parentreq->lr_child != NULL ) { + lr->lr_sibling = parentreq->lr_child; + } + parentreq->lr_child = lr; + } else { /* original request */ + lr->lr_origid = lr->lr_msgid; + } + + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + /* add new request to the end of the list of outstanding requests */ + nsldapi_queue_request_nolock( ld, lr ); + + /* + * Issue a non-blocking poll() if we need to check this + * connection's status. + */ + if ( lc->lconn_status == LDAP_CONNST_CONNECTING || + lc->lconn_pending_requests > 0 ) { + struct timeval tv; + + tv.tv_sec = tv.tv_usec = 0; + (void)nsldapi_iostatus_poll( ld, &tv ); + } + + /* + * If the connect is pending, check to see if it has now completed. + */ + if ( lc->lconn_status == LDAP_CONNST_CONNECTING && + nsldapi_iostatus_is_write_ready( ld, lc->lconn_sb )) { + lc->lconn_status = LDAP_CONNST_CONNECTED; + + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: connection 0x%p -" + " LDAP_CONNST_CONNECTING -> LDAP_CONNST_CONNECTED\n", + lc, 0, 0 ); + } + + if ( lc->lconn_status == LDAP_CONNST_CONNECTING || + lc->lconn_pending_requests > 0 ) { + /* + * The connect is not yet complete, or there are existing + * requests that have not yet been sent to the server. + * Delay sending this request. + */ + lr->lr_status = LDAP_REQST_WRITING; + ++lc->lconn_pending_requests; + nsldapi_iostatus_interest_write( ld, lc->lconn_sb ); + + /* + * If the connection is now connected, and it is ready + * to accept some more outbound data, send as many + * pending requests as possible. + */ + if ( lc->lconn_status != LDAP_CONNST_CONNECTING + && nsldapi_iostatus_is_write_ready( ld, lc->lconn_sb )) { + if ( nsldapi_send_pending_requests_nolock( ld, lc ) + == -1 ) { /* error */ + /* + * Since nsldapi_send_pending_requests_nolock() + * sets LDAP errno, there is no need to do so + * here. + */ + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + } + + } else { + if (( err = nsldapi_send_ber_message( ld, lc->lconn_sb, + ber, 0 /* do not free ber */, + 1 /* will handle EPIPE */ )) != 0 ) { + + epipe_err = LDAP_GET_ERRNO( ld ); + if ( epipe_err == EPIPE ) { + res_rc = nsldapi_result_nolock(ld, LDAP_RES_UNSOLICITED, 1, + 1, (struct timeval *) NULL, &ext_res); + if ( ( res_rc == LDAP_RES_EXTENDED ) && ext_res ) { + ext_res_rc = ldap_parse_extended_result( ld, ext_res, + &ext_oid, &ext_data, 0 ); + if ( ext_res_rc != LDAP_SUCCESS ) { + if ( ext_res ) { + ldap_msgfree( ext_res ); + } + nsldapi_connection_lost_nolock( ld, lc->lconn_sb ); + } else { +#ifdef LDAP_DEBUG + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: Unsolicited response\n", 0, 0, 0 ); + if ( ext_oid ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: Unsolicited response oid: %s\n", + ext_oid, 0, 0 ); + } + if ( ext_data && ext_data->bv_len && ext_data->bv_val ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: Unsolicited response len: %d\n", + ext_data->bv_len, 0, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: Unsolicited response val: %s\n", + ext_data->bv_val, 0, 0 ); + } + if ( !ext_oid && !ext_data ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_send_server_request: Unsolicited response is empty\n", + 0, 0, 0 ); + } +#endif /* LDAP_DEBUG */ + if ( ext_oid ) { + if ( strcmp ( ext_oid, + LDAP_NOTICE_OF_DISCONNECTION ) == 0 ) { + if ( ext_data ) { + ber_bvfree( ext_data ); + } + if ( ext_oid ) { + ldap_memfree( ext_oid ); + } + if ( ext_res ) { + ldap_msgfree( ext_res ); + } + nsldapi_connection_lost_nolock( ld, + lc->lconn_sb ); + nsldapi_free_request( ld, lr, 0 ); + nsldapi_free_connection( ld, lc, + NULL, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + } + } + } else { + if ( ext_res ) { + ldap_msgfree( ext_res ); + } + nsldapi_connection_lost_nolock( ld, lc->lconn_sb ); + } + } + + /* need to continue write later */ + if (err == -2 ) { + lr->lr_status = LDAP_REQST_WRITING; + ++lc->lconn_pending_requests; + nsldapi_iostatus_interest_write( ld, lc->lconn_sb ); + } else { + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); + nsldapi_free_request( ld, lr, 0 ); + nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + return( -1 ); + } + + } else { + if ( parentreq == NULL ) { + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; + } + + /* sent -- waiting for a response */ + if (ld->ld_options & LDAP_BITOPT_ASYNC) { + lc->lconn_status = LDAP_CONNST_CONNECTED; + } + + nsldapi_iostatus_interest_read( ld, lc->lconn_sb ); + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); + return( msgid ); +} + + +/* + * nsldapi_send_ber_message(): Attempt to send a BER-encoded message. + * If freeit is non-zero, ber is freed when the send succeeds. + * If errno is EPIPE and epipe_handler is set we let the caller + * deal with EPIPE and dont call lost_nolock here but the caller + * should call lost_nolock itself when done with handling EPIPE. + * + * Return values: + * 0: message sent successfully. + * -1: a fatal error occurred while trying to send. + * -2: async. I/O is enabled and the send would block. + */ +int +nsldapi_send_ber_message( LDAP *ld, Sockbuf *sb, BerElement *ber, int freeit, + int epipe_handler ) +{ + int rc = 0; /* optimistic */ + int async = ( 0 != (ld->ld_options & LDAP_BITOPT_ASYNC)); + int more_to_send = 1; + + while ( more_to_send) { + /* + * ber_flush() doesn't set errno on EOF, so we pre-set it to + * zero to avoid getting tricked by leftover "EAGAIN" errors + */ + LDAP_SET_ERRNO( ld, 0 ); + + if ( ber_flush( sb, ber, freeit ) == 0 ) { + more_to_send = 0; /* success */ + } else { + int terrno = LDAP_GET_ERRNO( ld ); + if ( NSLDAPI_ERRNO_IO_INPROGRESS( terrno )) { + if ( async ) { + rc = -2; + break; + } + } else { + if ( !(epipe_handler && ( terrno == EPIPE )) ) { + nsldapi_connection_lost_nolock( ld, sb ); + } + rc = -1; /* fatal error */ + break; + } + } + } + + return( rc ); +} + + +/* + * nsldapi_send_pending_requests_nolock(): Send one or more pending requests + * that are associated with connection 'lc'. + * + * Return values: 0 -- success. + * -1 -- fatal error; connection closed. + * + * Must be called with these two mutexes locked, in this order: + * LDAP_CONN_LOCK + * LDAP_REQ_LOCK + */ +int +nsldapi_send_pending_requests_nolock( LDAP *ld, LDAPConn *lc ) +{ + int err; + int waiting_for_a_response = 0; + int rc = 0; + LDAPRequest *lr; + char *logname = "nsldapi_send_pending_requests_nolock"; + + LDAPDebug( LDAP_DEBUG_TRACE, "%s\n", logname, 0, 0 ); + + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + /* + * This code relies on the fact that the ld_requests list + * is in order from oldest to newest request (the oldest + * requests that have not yet been sent to the server are + * sent first). + */ + if ( lr->lr_status == LDAP_REQST_WRITING + && lr->lr_conn == lc ) { + err = nsldapi_send_ber_message( ld, lc->lconn_sb, + lr->lr_ber, 0 /* do not free ber */, + 0 /* will not handle EPIPE */ ); + if ( err == 0 ) { /* send succeeded */ + LDAPDebug( LDAP_DEBUG_TRACE, + "%s: 0x%p SENT\n", logname, lr, 0 ); + lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; + lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; + lr->lr_status = LDAP_REQST_INPROGRESS; + --lc->lconn_pending_requests; + } else if ( err == -2 ) { /* would block */ + rc = 0; /* not an error */ + LDAPDebug( LDAP_DEBUG_TRACE, + "%s: 0x%p WOULD BLOCK\n", logname, lr, 0 ); + break; + } else { /* fatal error */ + LDAPDebug( LDAP_DEBUG_TRACE, + "%s: 0x%p FATAL ERROR\n", logname, lr, 0 ); + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, + NULL, NULL ); + nsldapi_free_request( ld, lr, 0 ); + lr = NULL; + nsldapi_free_connection( ld, lc, NULL, NULL, + 0, 0 ); + lc = NULL; + rc = -1; + break; + } + } + + if (lr->lr_status == LDAP_REQST_INPROGRESS ) { + if (lr->lr_expect_resp) { + ++waiting_for_a_response; + } else { + LDAPDebug( LDAP_DEBUG_TRACE, + "%s: 0x%p NO RESPONSE EXPECTED;" + " freeing request \n", logname, lr, 0 ); + nsldapi_free_request( ld, lr, 0 ); + lr = NULL; + } + } + } + + if ( lc != NULL ) { + if ( lc->lconn_pending_requests < 1 ) { + /* no need to poll for "write ready" any longer */ + nsldapi_iostatus_interest_clear( ld, lc->lconn_sb ); + } + + if ( waiting_for_a_response ) { + /* need to poll for "read ready" */ + nsldapi_iostatus_interest_read( ld, lc->lconn_sb ); + } + } + + LDAPDebug( LDAP_DEBUG_TRACE, "%s <- %d\n", logname, rc, 0 ); + return( rc ); +} + + +LDAPConn * +nsldapi_new_connection( LDAP *ld, LDAPServer **srvlistp, int use_ldsb, + int connect, int bind ) +{ + int rc = -1; + LDAPConn *lc; + LDAPServer *prevsrv, *srv; + Sockbuf *sb = NULL; + + /* + * make a new LDAP server connection + */ + if (( lc = (LDAPConn *)NSLDAPI_CALLOC( 1, sizeof( LDAPConn ))) == NULL + || ( !use_ldsb && ( sb = ber_sockbuf_alloc()) == NULL )) { + if ( lc != NULL ) { + NSLDAPI_FREE( (char *)lc ); + } + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( NULL ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + if ( !use_ldsb ) { + /* + * we have allocated a new sockbuf + * set I/O routines to match those in default LDAP sockbuf + */ + IFP sb_fn; + struct lber_x_ext_io_fns extiofns; + + extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + + if ( ber_sockbuf_get_option( ld->ld_sbp, + LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns ) == 0 ) { + ber_sockbuf_set_option( sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns ); + } + if ( ber_sockbuf_get_option( ld->ld_sbp, + LBER_SOCKBUF_OPT_READ_FN, (void *)&sb_fn ) == 0 + && sb_fn != NULL ) { + ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_READ_FN, + (void *)sb_fn ); + } + if ( ber_sockbuf_get_option( ld->ld_sbp, + LBER_SOCKBUF_OPT_WRITE_FN, (void *)&sb_fn ) == 0 + && sb_fn != NULL ) { + ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_WRITE_FN, + (void *)sb_fn ); + } + } + + lc->lconn_sb = ( use_ldsb ) ? ld->ld_sbp : sb; + lc->lconn_version = ld->ld_version; /* inherited */ + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + + if ( connect ) { + prevsrv = NULL; + /* + * save the return code for later + */ + for ( srv = *srvlistp; srv != NULL; srv = srv->lsrv_next ) { + rc = nsldapi_connect_to_host( ld, lc->lconn_sb, + srv->lsrv_host, srv->lsrv_port, + ( srv->lsrv_options & LDAP_SRV_OPT_SECURE ) != 0, + &lc->lconn_krbinstance ); + if (rc != -1) { + break; + } + prevsrv = srv; + } + + if ( srv == NULL ) { + if ( !use_ldsb ) { + NSLDAPI_FREE( (char *)lc->lconn_sb ); + } + NSLDAPI_FREE( (char *)lc ); + /* nsldapi_open_ldap_connection has already set ld_errno */ + return( NULL ); + } + + if ( prevsrv == NULL ) { + *srvlistp = srv->lsrv_next; + } else { + prevsrv->lsrv_next = srv->lsrv_next; + } + lc->lconn_server = srv; + } + + if ( 0 != (ld->ld_options & LDAP_BITOPT_ASYNC)) { + /* + * Technically, the socket may already be connected but we are + * not sure. By setting the state to LDAP_CONNST_CONNECTING, we + * ensure that we will check the socket status to make sure it + * is connected before we try to send any LDAP messages. + */ + lc->lconn_status = LDAP_CONNST_CONNECTING; + } else { + lc->lconn_status = LDAP_CONNST_CONNECTED; + } + + lc->lconn_next = ld->ld_conns; + ld->ld_conns = lc; + + /* + * XXX for now, we always do a synchronous bind. This will have + * to change in the long run... + */ + if ( bind ) { + int err, lderr, freepasswd, authmethod; + char *binddn, *passwd; + LDAPConn *savedefconn; + + freepasswd = err = 0; + + if ( ld->ld_rebind_fn == NULL ) { + binddn = passwd = ""; + authmethod = LDAP_AUTH_SIMPLE; + } else { + if (( lderr = (*ld->ld_rebind_fn)( ld, &binddn, &passwd, + &authmethod, 0, ld->ld_rebind_arg )) + == LDAP_SUCCESS ) { + freepasswd = 1; + } else { + LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); + err = -1; + } + } + + + if ( err == 0 ) { + savedefconn = ld->ld_defconn; + ld->ld_defconn = lc; + ++lc->lconn_refcnt; /* avoid premature free */ + + /* + * when binding, we will back down as low as LDAPv2 + * if we get back "protocol error" from bind attempts + */ + for ( ;; ) { + /* LDAP_MUTEX_UNLOCK(ld, LDAP_CONN_LOCK); */ + if (( lderr = ldap_bind_s( ld, binddn, passwd, + authmethod )) == LDAP_SUCCESS ) { + /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */ + break; + } + /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */ + if ( lc->lconn_version <= LDAP_VERSION2 + || lderr != LDAP_PROTOCOL_ERROR ) { + err = -1; + break; + } + --lc->lconn_version; /* try lower version */ + } + --lc->lconn_refcnt; + ld->ld_defconn = savedefconn; + } + + if ( freepasswd ) { + (*ld->ld_rebind_fn)( ld, &binddn, &passwd, + &authmethod, 1, ld->ld_rebind_arg ); + } + + if ( err != 0 ) { + nsldapi_free_connection( ld, lc, NULL, NULL, 1, 0 ); + lc = NULL; + } + } + + return( lc ); +} + + +#define LDAP_CONN_SAMEHOST( h1, h2 ) \ + (( (h1) == NULL && (h2) == NULL ) || \ + ( (h1) != NULL && (h2) != NULL && strcasecmp( (h1), (h2) ) == 0 )) + +static LDAPConn * +find_connection( LDAP *ld, LDAPServer *srv, int any ) +/* + * return an existing connection (if any) to the server srv + * if "any" is non-zero, check for any server in the "srv" chain + */ +{ + LDAPConn *lc; + LDAPServer *ls; + + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + for ( ls = srv; ls != NULL; ls = ls->lsrv_next ) { + if ( LDAP_CONN_SAMEHOST( ls->lsrv_host, + lc->lconn_server->lsrv_host ) + && ls->lsrv_port == lc->lconn_server->lsrv_port + && ls->lsrv_options == + lc->lconn_server->lsrv_options ) { + return( lc ); + } + if ( !any ) { + break; + } + } + } + + return( NULL ); +} + + +void +nsldapi_free_connection( LDAP *ld, LDAPConn *lc, LDAPControl **serverctrls, + LDAPControl **clientctrls, int force, int unbind ) +{ + LDAPConn *tmplc, *prevlc; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection\n", 0, 0, 0 ); + + if ( force || --lc->lconn_refcnt <= 0 ) { + nsldapi_iostatus_interest_clear( ld, lc->lconn_sb ); + if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { + if ( unbind ) { + nsldapi_send_unbind( ld, lc->lconn_sb, + serverctrls, clientctrls ); + } + } + nsldapi_close_connection( ld, lc->lconn_sb ); + prevlc = NULL; + for ( tmplc = ld->ld_conns; tmplc != NULL; + tmplc = tmplc->lconn_next ) { + if ( tmplc == lc ) { + if ( prevlc == NULL ) { + ld->ld_conns = tmplc->lconn_next; + } else { + prevlc->lconn_next = tmplc->lconn_next; + } + break; + } + prevlc = tmplc; + } + free_servers( lc->lconn_server ); + if ( lc->lconn_krbinstance != NULL ) { + NSLDAPI_FREE( lc->lconn_krbinstance ); + } + /* + * if this is the default connection (lc->lconn_sb==ld->ld_sbp) + * we do not free the Sockbuf here since it will be freed + * later inside ldap_unbind(). + */ + if ( lc->lconn_sb != ld->ld_sbp ) { + ber_sockbuf_free( lc->lconn_sb ); + lc->lconn_sb = NULL; + } + if ( lc->lconn_ber != NULLBER ) { + ber_free( lc->lconn_ber, 1 ); + } + if ( lc->lconn_binddn != NULL ) { + NSLDAPI_FREE( lc->lconn_binddn ); + } +#ifdef LDAP_SASLIO_HOOKS + if ( lc->lconn_sasl_ctx ) { /* the sasl connection context */ + sasl_dispose(&lc->lconn_sasl_ctx); + lc->lconn_sasl_ctx = NULL; + } +#endif /* LDAP_SASLIO_HOOKS */ + NSLDAPI_FREE( lc ); + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection: actually freed\n", + 0, 0, 0 ); + } else { + lc->lconn_lastused = time( 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_connection: refcnt %d\n", + lc->lconn_refcnt, 0, 0 ); + } +} + + +#ifdef LDAP_DEBUG +void +nsldapi_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) +{ + LDAPConn *lc; + char msg[256]; +/* CTIME for this platform doesn't use this. */ +#if !defined(SUNOS4) && !defined(_WIN32) && !defined(LINUX) && !defined(macintosh) + char buf[26]; +#endif + + sprintf( msg, "** Connection%s:\n", all ? "s" : "" ); + ber_err_print( msg ); + for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { + if ( lc->lconn_server != NULL ) { + sprintf( msg, "* 0x%p - host: %s port: %d secure: %s%s\n", + lc, ( lc->lconn_server->lsrv_host == NULL ) ? "(null)" + : lc->lconn_server->lsrv_host, + lc->lconn_server->lsrv_port, + ( lc->lconn_server->lsrv_options & + LDAP_SRV_OPT_SECURE ) ? "Yes" : + "No", ( lc->lconn_sb == ld->ld_sbp ) ? + " (default)" : "" ); + ber_err_print( msg ); + } + sprintf( msg, " refcnt: %d pending: %d status: %s\n", + lc->lconn_refcnt, lc->lconn_pending_requests, + ( lc->lconn_status == LDAP_CONNST_CONNECTING ) + ? "Connecting" : + ( lc->lconn_status == LDAP_CONNST_DEAD ) ? "Dead" : + "Connected" ); + ber_err_print( msg ); + sprintf( msg, " last used: %s", + NSLDAPI_CTIME( (time_t *) &lc->lconn_lastused, buf, + sizeof(buf) )); + ber_err_print( msg ); + if ( lc->lconn_ber != NULLBER ) { + ber_err_print( " partial response has been received:\n" ); + ber_dump( lc->lconn_ber, 1 ); + } + ber_err_print( "\n" ); + + if ( !all ) { + break; + } + } +} + + +void +nsldapi_dump_requests_and_responses( LDAP *ld ) +{ + LDAPRequest *lr; + LDAPMessage *lm, *l; + char msg[256]; + + ber_err_print( "** Outstanding Requests:\n" ); + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + if (( lr = ld->ld_requests ) == NULL ) { + ber_err_print( " Empty\n" ); + } + for ( ; lr != NULL; lr = lr->lr_next ) { + sprintf( msg, " * 0x%p - msgid %d, origid %d, status %s\n", + lr, lr->lr_msgid, lr->lr_origid, ( lr->lr_status == + LDAP_REQST_INPROGRESS ) ? "InProgress" : + ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" : + ( lr->lr_status == LDAP_REQST_CONNDEAD ) ? "Dead" : + "Writing" ); + ber_err_print( msg ); + sprintf( msg, " outstanding referrals %d, parent count %d\n", + lr->lr_outrefcnt, lr->lr_parentcnt ); + ber_err_print( msg ); + if ( lr->lr_binddn != NULL ) { + sprintf( msg, " pending bind DN: <%s>\n", lr->lr_binddn ); + ber_err_print( msg ); + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + + ber_err_print( "** Response Queue:\n" ); + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + if (( lm = ld->ld_responses ) == NULLMSG ) { + ber_err_print( " Empty\n" ); + } + for ( ; lm != NULLMSG; lm = lm->lm_next ) { + sprintf( msg, " * 0x%p - msgid %d, type %d\n", + lm, lm->lm_msgid, lm->lm_msgtype ); + ber_err_print( msg ); + if (( l = lm->lm_chain ) != NULL ) { + ber_err_print( " chained responses:\n" ); + for ( ; l != NULLMSG; l = l->lm_chain ) { + sprintf( msg, + " * 0x%p - msgid %d, type %d\n", + l, l->lm_msgid, l->lm_msgtype ); + ber_err_print( msg ); + } + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); +} +#endif /* LDAP_DEBUG */ + + +LDAPRequest * +nsldapi_new_request( LDAPConn *lc, BerElement *ber, int msgid, int expect_resp ) +{ + LDAPRequest *lr; + + lr = (LDAPRequest *)NSLDAPI_CALLOC( 1, sizeof( LDAPRequest )); + + if ( lr != NULL ) { + lr->lr_conn = lc; + lr->lr_ber = ber; + lr->lr_msgid = lr->lr_origid = msgid; + lr->lr_expect_resp = expect_resp; + lr->lr_status = LDAP_REQST_INPROGRESS; + lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ + + if ( lc != NULL ) { /* mark connection as in use */ + ++lc->lconn_refcnt; + lc->lconn_lastused = time( 0 ); + } + } + + return( lr ); +} + + +void +nsldapi_free_request( LDAP *ld, LDAPRequest *lr, int free_conn ) +{ + LDAPRequest *tmplr, *nextlr; + + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_free_request 0x%p (origid %d, msgid %d)\n", + lr, lr->lr_origid, lr->lr_msgid ); + + if ( lr->lr_parent != NULL ) { + /* unlink child from parent */ + lr->lr_parent->lr_child = NULL; + --lr->lr_parent->lr_outrefcnt; + } + + if ( lr->lr_status == LDAP_REQST_WRITING ) { + --lr->lr_conn->lconn_pending_requests; + } + + /* free all of our spawned referrals (child requests) */ + for ( tmplr = lr->lr_child; tmplr != NULL; tmplr = nextlr ) { + nextlr = tmplr->lr_sibling; + nsldapi_free_request( ld, tmplr, free_conn ); + } + + if ( free_conn ) { + nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL, 0, 1 ); + } + + if ( lr->lr_prev == NULL ) { + ld->ld_requests = lr->lr_next; + } else { + lr->lr_prev->lr_next = lr->lr_next; + } + + if ( lr->lr_next != NULL ) { + lr->lr_next->lr_prev = lr->lr_prev; + } + + if ( lr->lr_ber != NULL ) { + ber_free( lr->lr_ber, 1 ); + } + + if ( lr->lr_res_error != NULL ) { + NSLDAPI_FREE( lr->lr_res_error ); + } + + if ( lr->lr_res_matched != NULL ) { + NSLDAPI_FREE( lr->lr_res_matched ); + } + + if ( lr->lr_binddn != NULL ) { + NSLDAPI_FREE( lr->lr_binddn ); + } + + if ( lr->lr_res_ctrls != NULL ) { + ldap_controls_free( lr->lr_res_ctrls ); + } + NSLDAPI_FREE( lr ); +} + + +/* + * Add a request to the end of the list of outstanding requests. + * This function must be called with these two locks in hand, acquired in + * this order: + * LDAP_CONN_LOCK + * LDAP_REQ_LOCK + */ +void +nsldapi_queue_request_nolock( LDAP *ld, LDAPRequest *lr ) +{ + if ( NULL == ld->ld_requests ) { + ld->ld_requests = lr; + } else { + LDAPRequest *tmplr; + + for ( tmplr = ld->ld_requests; tmplr->lr_next != NULL; + tmplr = tmplr->lr_next ) { + ; + } + tmplr->lr_next = lr; + lr->lr_prev = tmplr; + } +} + + +static void +free_servers( LDAPServer *srvlist ) +{ + LDAPServer *nextsrv; + + while ( srvlist != NULL ) { + nextsrv = srvlist->lsrv_next; + if ( srvlist->lsrv_dn != NULL ) { + NSLDAPI_FREE( srvlist->lsrv_dn ); + } + if ( srvlist->lsrv_host != NULL ) { + NSLDAPI_FREE( srvlist->lsrv_host ); + } + NSLDAPI_FREE( srvlist ); + srvlist = nextsrv; + } +} + + +/* + * Initiate chasing of LDAPv2+ (Umich extension) referrals. + * + * Returns an LDAP error code. + * + * Note that *hadrefp will be set to 1 if one or more referrals were found in + * "*errstrp" (even if we can't chase them) and zero if none were found. + * + * XXX merging of errors in this routine needs to be improved. + */ +int +nsldapi_chase_v2_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, + int *totalcountp, int *chasingcountp ) +{ + char *p, *ref, *unfollowed; + LDAPRequest *origreq; + int rc, tmprc, len, unknown; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_chase_v2_referrals\n", 0, 0, 0 ); + + *totalcountp = *chasingcountp = 0; + + if ( *errstrp == NULL ) { + return( LDAP_SUCCESS ); + } + + len = strlen( *errstrp ); + for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) { + if (( *p == 'R' || *p == 'r' ) && strncasecmp( p, + LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { + *p = '\0'; + p += LDAP_REF_STR_LEN; + break; + } + } + + if ( len < LDAP_REF_STR_LEN ) { + return( LDAP_SUCCESS ); + } + + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "more than %d referral hops (dropping)\n", + ld->ld_refhoplimit, 0, 0 ); + return( LDAP_REFERRAL_LIMIT_EXCEEDED ); + } + + /* find original request */ + for ( origreq = lr; origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) { + ; + } + + unfollowed = NULL; + rc = LDAP_SUCCESS; + + /* parse out & follow referrals */ + for ( ref = p; rc == LDAP_SUCCESS && ref != NULL; ref = p ) { + if (( p = strchr( ref, '\n' )) != NULL ) { + *p++ = '\0'; + } else { + p = NULL; + } + + ++*totalcountp; + + rc = chase_one_referral( ld, lr, origreq, ref, "v2 referral", + &unknown, 0 /* not a reference */ ); + + if ( rc != LDAP_SUCCESS || unknown ) { + if (( tmprc = nsldapi_append_referral( ld, &unfollowed, + ref )) != LDAP_SUCCESS ) { + rc = tmprc; + } + } else { + ++*chasingcountp; + } + } + + NSLDAPI_FREE( *errstrp ); + *errstrp = unfollowed; + + return( rc ); +} + + +/* returns an LDAP error code */ +int +nsldapi_chase_v3_refs( LDAP *ld, LDAPRequest *lr, char **v3refs, + int is_reference, int *totalcountp, int *chasingcountp ) +{ + int rc = LDAP_SUCCESS; + int i, unknown; + LDAPRequest *origreq; + + *totalcountp = *chasingcountp = 0; + + if ( v3refs == NULL || v3refs[0] == NULL ) { + return( LDAP_SUCCESS ); + } + + *totalcountp = 1; + + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "more than %d referral hops (dropping)\n", + ld->ld_refhoplimit, 0, 0 ); + return( LDAP_REFERRAL_LIMIT_EXCEEDED ); + } + + /* find original request */ + for ( origreq = lr; origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) { + ; + } + + /* + * in LDAPv3, we just need to follow one referral in the set. + * we dp this by stopping as soon as we succeed in initiating a + * chase on any referral (basically this means we were able to connect + * to the server and bind). + */ + for ( i = 0; v3refs[i] != NULL; ++i ) { + rc = chase_one_referral( ld, lr, origreq, v3refs[i], + is_reference ? "v3 reference" : "v3 referral", &unknown, + is_reference ); + if ( rc == LDAP_SUCCESS && !unknown ) { + *chasingcountp = 1; + break; + } + } + + /* XXXmcs: should we save unfollowed referrals somewhere? */ + + return( rc ); /* last error is as good as any other I guess... */ +} + + +/* + * returns an LDAP error code + * + * XXXmcs: this function used to have #ifdef LDAP_DNS code in it but I + * removed it when I improved the parsing (we don't define LDAP_DNS + * here at Netscape). + */ +static int +chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq, + char *refurl, char *desc, int *unknownp, int is_reference ) +{ + int rc, tmprc, secure, msgid; + LDAPServer *srv; + BerElement *ber; + LDAPURLDesc *ludp; + + *unknownp = 0; + ludp = NULLLDAPURLDESC; + + if ( nsldapi_url_parse( refurl, &ludp, 0 ) != 0 ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "ignoring unknown %s <%s>\n", desc, refurl, 0 ); + *unknownp = 1; + rc = LDAP_SUCCESS; + goto cleanup_and_return; + } + + secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 ); + +/* XXXmcs: can't tell if secure is supported by connect callback */ + if ( secure && ld->ld_extconnect_fn == NULL ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "ignoring LDAPS %s <%s>\n", desc, refurl, 0 ); + *unknownp = 1; + rc = LDAP_SUCCESS; + goto cleanup_and_return; + } + + LDAPDebug( LDAP_DEBUG_TRACE, "chasing LDAP%s %s: <%s>\n", + secure ? "S" : "", desc, refurl ); + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + if (( tmprc = re_encode_request( ld, origreq->lr_ber, msgid, + ludp, &ber, is_reference )) != LDAP_SUCCESS ) { + rc = tmprc; + goto cleanup_and_return; + } + + if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer ))) + == NULL ) { + ber_free( ber, 1 ); + rc = LDAP_NO_MEMORY; + goto cleanup_and_return; + } + + if ( ludp->lud_host == NULL && ld->ld_defhost == NULL ) { + srv->lsrv_host = NULL; + } else { + if ( ludp->lud_host == NULL ) { + srv->lsrv_host = + nsldapi_strdup( origreq->lr_conn->lconn_server->lsrv_host ); + LDAPDebug( LDAP_DEBUG_TRACE, + "chase_one_referral: using hostname '%s' from original " + "request on new request\n", + srv->lsrv_host, 0, 0); + } else { + srv->lsrv_host = nsldapi_strdup( ludp->lud_host ); + LDAPDebug( LDAP_DEBUG_TRACE, + "chase_one_referral: using hostname '%s' as specified " + "on new request\n", + srv->lsrv_host, 0, 0); + } + + if ( srv->lsrv_host == NULL ) { + NSLDAPI_FREE( (char *)srv ); + ber_free( ber, 1 ); + rc = LDAP_NO_MEMORY; + goto cleanup_and_return; + } + } + + if ( ludp->lud_port == 0 && ludp->lud_host == NULL ) { + srv->lsrv_port = origreq->lr_conn->lconn_server->lsrv_port; + LDAPDebug( LDAP_DEBUG_TRACE, + "chase_one_referral: using port (%d) from original " + "request on new request\n", + srv->lsrv_port, 0, 0); + } else if ( ludp->lud_port != 0 ) { + srv->lsrv_port = ludp->lud_port; + LDAPDebug( LDAP_DEBUG_TRACE, + "chase_one_referral: using port (%d) as specified on " + "new request\n", + srv->lsrv_port, 0, 0); + } else { + srv->lsrv_port = secure ? LDAPS_PORT : LDAP_PORT; + LDAPDebug( LDAP_DEBUG_TRACE, + "chase_one_referral: using default port (%d)\n", + srv->lsrv_port, 0, 0 ); + } + + if ( secure ) { + srv->lsrv_options |= LDAP_SRV_OPT_SECURE; + } + + if ( nsldapi_send_server_request( ld, ber, msgid, + lr, srv, NULL, NULL, 1 ) < 0 ) { + rc = LDAP_GET_LDERRNO( ld, NULL, NULL ); + LDAPDebug( LDAP_DEBUG_ANY, "Unable to chase %s %s (%s)\n", + desc, refurl, ldap_err2string( rc )); + } else { + rc = LDAP_SUCCESS; + } + +cleanup_and_return: + if ( ludp != NULLLDAPURLDESC ) { + ldap_free_urldesc( ludp ); + } + + return( rc ); +} + + +/* returns an LDAP error code */ +int +nsldapi_append_referral( LDAP *ld, char **referralsp, char *s ) +{ + int first; + + if ( *referralsp == NULL ) { + first = 1; + *referralsp = (char *)NSLDAPI_MALLOC( strlen( s ) + + LDAP_REF_STR_LEN + 1 ); + } else { + first = 0; + *referralsp = (char *)NSLDAPI_REALLOC( *referralsp, + strlen( *referralsp ) + strlen( s ) + 2 ); + } + + if ( *referralsp == NULL ) { + return( LDAP_NO_MEMORY ); + } + + if ( first ) { + strcpy( *referralsp, LDAP_REF_STR ); + } else { + strcat( *referralsp, "\n" ); + } + strcat( *referralsp, s ); + + return( LDAP_SUCCESS ); +} + + + +/* returns an LDAP error code */ +static int +re_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *ludp, + BerElement **berp, int is_reference ) +{ +/* + * XXX this routine knows way too much about how the lber library works! + */ + ber_int_t origmsgid; + ber_tag_t tag; + ber_int_t ver; + int rc; + BerElement *ber; + struct berelement tmpber; + char *dn, *orig_dn; + /* extra stuff for search request */ + ber_int_t scope = -1; + + LDAPDebug( LDAP_DEBUG_TRACE, + "re_encode_request: new msgid %d, new dn <%s>\n", + msgid, ( ludp->lud_dn == NULL ) ? "NONE" : ludp->lud_dn, 0 ); + + tmpber = *origber; + + /* + * All LDAP requests are sequences that start with a message id. For + * everything except delete requests, this is followed by a sequence + * that is tagged with the operation code. For deletes, there is just + * a DN that is tagged with the operation code. + */ + + /* skip past msgid and get operation tag */ + if ( ber_scanf( &tmpber, "{it", &origmsgid, &tag ) == LBER_ERROR ) { + return( LDAP_DECODING_ERROR ); + } + + /* + * XXXmcs: we don't support filters in search referrals yet, + * so if present we return an error which is probably + * better than just ignoring the extra info. + * XXXrichm: we now support scopes. Supporting filters would require + * a lot more additional work to be able to read the filter from + * the ber original search request, convert to string, etc. It might + * be better and easier to change nsldapi_build_search_req to have + * some special mode by which you could tell it to skip filter and + * attrlist encoding if no filter was given - then we could just + * create a new ber search request with our new filter if present. + */ + if ( ( tag == LDAP_REQ_SEARCH ) && ( ludp->lud_filter != NULL )) { + return( LDAP_LOCAL_ERROR ); + } + + if ( tag == LDAP_REQ_BIND ) { + /* bind requests have a version number before the DN */ + rc = ber_scanf( &tmpber, "{ia", &ver, &orig_dn ); + } else if ( tag == LDAP_REQ_DELETE ) { + /* delete requests DNs are not within a sequence */ + rc = ber_scanf( &tmpber, "a", &orig_dn ); + } else if ( tag == LDAP_REQ_SEARCH ) { + /* need scope */ + rc = ber_scanf( &tmpber, "{ae", &orig_dn, &scope ); + } else { + rc = ber_scanf( &tmpber, "{a", &orig_dn ); + } + + if ( rc == LBER_ERROR ) { + return( LDAP_DECODING_ERROR ); + } + + if ( ludp->lud_dn == NULL ) { + dn = orig_dn; + } else { + dn = ludp->lud_dn; + NSLDAPI_FREE( orig_dn ); + orig_dn = NULL; + } + + if ( ludp->lud_scope != -1 ) { + scope = ludp->lud_scope; /* scope provided by ref - use it */ + } else if (is_reference) { + /* + * RFC 4511 says that the we should use scope BASE in the + * search reference if the client's original request was for scope + * ONELEVEL - since the server did not include the scope in the + * search reference returned, we must provide the correct behavior + * in the client (i.e. the correct behavior is implied) + * see RFC 4511 section 4.5.3 for more information + */ + if (scope == LDAP_SCOPE_ONELEVEL) { + scope = LDAP_SCOPE_BASE; + } + } + + /* allocate and build the new request */ + if (( rc = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + if ( orig_dn != NULL ) { + NSLDAPI_FREE( orig_dn ); + } + return( rc ); + } + + if ( tag == LDAP_REQ_BIND ) { + rc = ber_printf( ber, "{it{is", msgid, tag, ver , dn ); + } else if ( tag == LDAP_REQ_DELETE ) { + rc = ber_printf( ber, "{its}", msgid, tag, dn ); + } else if ( tag == LDAP_REQ_SEARCH ) { + rc = ber_printf( ber, "{it{se", msgid, tag, dn, scope ); + } else { + rc = ber_printf( ber, "{it{s", msgid, tag, dn ); + } + + if ( orig_dn != NULL ) { + NSLDAPI_FREE( orig_dn ); + } +/* + * can't use "dn" or "orig_dn" from this point on (they've been freed) + */ + + if ( rc == -1 ) { + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if ( tag != LDAP_REQ_DELETE && + ( ber_write( ber, tmpber.ber_ptr, ( tmpber.ber_end - + tmpber.ber_ptr ), 0 ) != ( tmpber.ber_end - tmpber.ber_ptr ) + || ber_printf( ber, "}}" ) == -1 )) { + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + LDAPDebug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n", + 0, 0, 0 ); + ber_dump( ber, 0 ); + } +#endif /* LDAP_DEBUG */ + + *berp = ber; + return( LDAP_SUCCESS ); +} + + +LDAPRequest * +nsldapi_find_request_by_msgid( LDAP *ld, int msgid ) +{ + LDAPRequest *lr; + + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + if ( msgid == lr->lr_msgid ) { + break; + } + } + + return( lr ); +} + + +/* + * nsldapi_connection_lost_nolock() resets "ld" to a non-connected, known + * state. It should be called whenever a fatal error occurs on the + * Sockbuf "sb." sb == NULL means we don't know specifically where + * the problem was so we assume all connections are bad. + */ +void +nsldapi_connection_lost_nolock( LDAP *ld, Sockbuf *sb ) +{ + LDAPRequest *lr; + + /* + * change status of all pending requests that are associated with "sb + * to "connection dead." + * also change the connection status to "dead" and remove it from + * the list of sockets we are interested in. + */ + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + if ( sb == NULL || + ( lr->lr_conn != NULL && lr->lr_conn->lconn_sb == sb )) { + lr->lr_status = LDAP_REQST_CONNDEAD; + if ( lr->lr_conn != NULL ) { + lr->lr_conn->lconn_status = LDAP_CONNST_DEAD; + nsldapi_iostatus_interest_clear( ld, + lr->lr_conn->lconn_sb ); + } + } + } +} + + +#ifdef LDAP_DNS +static LDAPServer * +dn2servers( LDAP *ld, char *dn ) /* dn can also be a domain.... */ +{ + char *p, *domain, *host, *server_dn, **dxs; + int i, port; + LDAPServer *srvlist, *prevsrv, *srv; + + if (( domain = strrchr( dn, '@' )) != NULL ) { + ++domain; + } else { + domain = dn; + } + + if (( dxs = nsldapi_getdxbyname( domain )) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( NULL ); + } + + srvlist = NULL; + + for ( i = 0; dxs[ i ] != NULL; ++i ) { + port = LDAP_PORT; + server_dn = NULL; + if ( strchr( dxs[ i ], ':' ) == NULL ) { + host = dxs[ i ]; + } else if ( strlen( dxs[ i ] ) >= 7 && + strncmp( dxs[ i ], "ldap://", 7 ) == 0 ) { + host = dxs[ i ] + 7; + if (( p = strchr( host, ':' )) == NULL ) { + p = host; + } else { + *p++ = '\0'; + port = atoi( p ); + } + if (( p = strchr( p, '/' )) != NULL ) { + server_dn = ++p; + if ( *server_dn == '\0' ) { + server_dn = NULL; + } + } + } else { + host = NULL; + } + + if ( host != NULL ) { /* found a server we can use */ + if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, + sizeof( LDAPServer ))) == NULL ) { + free_servers( srvlist ); + srvlist = NULL; + break; /* exit loop & return */ + } + + /* add to end of list of servers */ + if ( srvlist == NULL ) { + srvlist = srv; + } else { + prevsrv->lsrv_next = srv; + } + prevsrv = srv; + + /* copy in info. */ + if (( srv->lsrv_host = nsldapi_strdup( host )) == NULL + || ( server_dn != NULL && ( srv->lsrv_dn = + nsldapi_strdup( server_dn )) == NULL )) { + free_servers( srvlist ); + srvlist = NULL; + break; /* exit loop & return */ + } + srv->lsrv_port = port; + } + } + + ldap_value_free( dxs ); + + if ( srvlist == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); + } + + return( srvlist ); +} +#endif /* LDAP_DNS */ diff --git a/ldap/c-sdk/libraries/libldap/reslist.c b/ldap/c-sdk/libraries/libldap/reslist.c new file mode 100644 index 0000000000..a382941eba --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/reslist.c @@ -0,0 +1,86 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * reslist.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +LDAPMessage * +LDAP_CALL +ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + LDAPMessage *tmp, *prev = NULL; + + if ( list == NULL || e == NULL ) { + return( NULL ); + } + + for ( tmp = *list; tmp != NULL && tmp != e; tmp = tmp->lm_chain ) + prev = tmp; + + if ( tmp == NULL ) + return( NULL ); + + if ( prev == NULL ) + *list = tmp->lm_chain; + else + prev->lm_chain = tmp->lm_chain; + tmp->lm_chain = NULL; + + return( tmp ); +} + +void +LDAP_CALL +ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + if ( list != NULL && e != NULL ) { + e->lm_chain = *list; + *list = e; + } +} diff --git a/ldap/c-sdk/libraries/libldap/result.c b/ldap/c-sdk/libraries/libldap/result.c new file mode 100644 index 0000000000..59c15f8115 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/result.c @@ -0,0 +1,1473 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * result.c - wait for an ldap result + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +/* + * Special return values used by some functions (wait4msg() and read1msg()). + */ +#define NSLDAPI_RESULT_TIMEOUT 0 +#define NSLDAPI_RESULT_ERROR (-1) +#define NSLDAPI_RESULT_NOT_FOUND (-2) + +static int check_response_queue( LDAP *ld, int msgid, int all, + int do_abandon_check, LDAPMessage **result ); +static int ldap_abandoned( LDAP *ld, int msgid ); +static int ldap_mark_abandoned( LDAP *ld, int msgid ); +static int wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted, + struct timeval *timeout, LDAPMessage **result ); +static int read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn **lcp, + LDAPMessage **result ); +static void check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber, + int ldapversion, int *totalcountp, int *chasingcountp ); +static int build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr ); +static void merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ); +#if defined( CLDAP ) +static int cldap_select1( LDAP *ld, struct timeval *timeout ); +#endif +static void link_pend( LDAP *ld, LDAPPend *lp ); + +/* + * ldap_result - wait for an ldap result response to a message from the + * ldap server. If msgid is -1, any message will be accepted, otherwise + * ldap_result will wait for a response with msgid. If all is 0 the + * first message with id msgid will be accepted, otherwise, ldap_result + * will wait for all responses with id msgid and then return a pointer to + * the entire list of messages. This is only useful for search responses, + * which can be of two message types (zero or more entries, followed by an + * ldap result). The type of the first message received is returned. + * When waiting, any messages that have been abandoned are discarded. + * + * Example: + * ldap_result( s, msgid, all, timeout, result ) + */ +int +LDAP_CALL +ldap_result( + LDAP *ld, + int msgid, + int all, + struct timeval *timeout, + LDAPMessage **result +) +{ + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_result\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); /* punt */ + } + + LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK ); + + rc = nsldapi_result_nolock(ld, msgid, all, 1, timeout, result); + + LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK ); + + return( rc ); +} + + +int +nsldapi_result_nolock( LDAP *ld, int msgid, int all, int unlock_permitted, + struct timeval *timeout, LDAPMessage **result ) +{ + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_result_nolock (msgid=%d, all=%d)\n", msgid, all, 0 ); + + /* + * First, look through the list of responses we have received on + * this association and see if the response we're interested in + * is there. If it is, return it. If not, call wait4msg() to + * wait until it arrives or timeout occurs. + */ + + if ( result == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( -1 ); + } + + if ( check_response_queue( ld, msgid, all, 1, result ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); + rc = (*result)->lm_msgtype; + } else { + rc = wait4msg( ld, msgid, all, unlock_permitted, timeout, + result ); + } + + /* + * XXXmcs should use cache function pointers to hook in memcache + */ + if ( ld->ld_memcache != NULL && NSLDAPI_SEARCH_RELATED_RESULT( rc ) && + !((*result)->lm_fromcache )) { + ldap_memcache_append( ld, (*result)->lm_msgid, + (all || NSLDAPI_IS_SEARCH_RESULT( rc )), *result ); + } + + return( rc ); +} + + +/* + * Look through the list of queued responses for a message that matches the + * criteria in the msgid and all parameters. msgid == LDAP_RES_ANY matches + * all ids. + * + * If an appropriate message is found, a non-zero value is returned and the + * message is dequeued and assigned to *result. + * + * If not, *result is set to NULL and this function returns 0. + */ +static int +check_response_queue( LDAP *ld, int msgid, int all, int do_abandon_check, + LDAPMessage **result ) +{ + LDAPMessage *lm, *lastlm, *nextlm; + LDAPRequest *lr; + + LDAPDebug( LDAP_DEBUG_TRACE, + "=> check_response_queue (msgid=%d, all=%d)\n", msgid, all, 0 ); + + *result = NULL; + lastlm = NULL; + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { + nextlm = lm->lm_next; + + if ( do_abandon_check && ldap_abandoned( ld, lm->lm_msgid ) ) { + ldap_mark_abandoned( ld, lm->lm_msgid ); + + if ( lastlm == NULL ) { + ld->ld_responses = lm->lm_next; + } else { + lastlm->lm_next = nextlm; + } + + ldap_msgfree( lm ); + + continue; + } + + if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) { + LDAPMessage *tmp; + + if ( all == 0 + || (lm->lm_msgtype != LDAP_RES_SEARCH_RESULT + && lm->lm_msgtype != LDAP_RES_SEARCH_REFERENCE + && lm->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) + break; + + for ( tmp = lm; tmp != NULL; tmp = tmp->lm_chain ) { + if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) + break; + } + + if ( tmp == NULL ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + LDAPDebug( LDAP_DEBUG_TRACE, + "<= check_response_queue NOT FOUND\n", + 0, 0, 0 ); + return( 0 ); /* no message to return */ + } + + break; + } + lastlm = lm; + } + + /* + * if we did not find a message OR if the one we found is a result for + * a request that is still pending, return failure. + */ + if ( lm == NULL + || (( lr = nsldapi_find_request_by_msgid( ld, lm->lm_msgid )) + != NULL && lr->lr_outrefcnt > 0 )) { + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + LDAPDebug( LDAP_DEBUG_TRACE, + "<= check_response_queue NOT FOUND\n", + 0, 0, 0 ); + return( 0 ); /* no message to return */ + } + + if ( all == 0 ) { + if ( lm->lm_chain == NULL ) { + if ( lastlm == NULL ) { + ld->ld_responses = lm->lm_next; + } else { + lastlm->lm_next = lm->lm_next; + } + } else { + if ( lastlm == NULL ) { + ld->ld_responses = lm->lm_chain; + ld->ld_responses->lm_next = lm->lm_next; + } else { + lastlm->lm_next = lm->lm_chain; + lastlm->lm_next->lm_next = lm->lm_next; + } + } + } else { + if ( lastlm == NULL ) { + ld->ld_responses = lm->lm_next; + } else { + lastlm->lm_next = lm->lm_next; + } + } + + if ( all == 0 ) { + lm->lm_chain = NULL; + } + lm->lm_next = NULL; + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + + *result = lm; + LDAPDebug( LDAP_DEBUG_TRACE, + "<= check_response_queue returning msgid %d type %d\n", + lm->lm_msgid, lm->lm_msgtype, 0 ); + return( 1 ); /* a message was found and returned in *result */ +} + + +/* + * wait4msg(): Poll for incoming LDAP messages, respecting the timeout. + * + * Return values: + * > 0: message received; value is the tag of the message. + * NSLDAPI_RESULT_TIMEOUT timeout exceeded. + * NSLDAPI_RESULT_ERROR fatal error occurred such as connection closed. + */ +static int +wait4msg( LDAP *ld, int msgid, int all, int unlock_permitted, + struct timeval *timeout, LDAPMessage **result ) +{ + int err, rc = NSLDAPI_RESULT_NOT_FOUND, msgfound; + struct timeval tv, *tvp; + long start_time = 0, tmp_time; + LDAPConn *lc, *nextlc; + /* lr points to the specific request we are waiting for, if any */ + LDAPRequest *lr = NULL; + +#ifdef LDAP_DEBUG + if ( timeout == NULL ) { + LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (infinite timeout)\n", + 0, 0, 0 ); + } else { + LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg (timeout %ld sec, %ld usec)\n", + timeout->tv_sec, (long) timeout->tv_usec, 0 ); + } +#endif /* LDAP_DEBUG */ + + /* check the cache */ + if ( ld->ld_cache_on && ld->ld_cache_result != NULL ) { + /* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */ + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + rc = (ld->ld_cache_result)( ld, msgid, all, timeout, result ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + /* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */ + if ( rc != NSLDAPI_RESULT_TIMEOUT ) { + return( rc ); + } + if ( ld->ld_cache_strategy == LDAP_CACHE_LOCALDB ) { + LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL ); + return( NSLDAPI_RESULT_TIMEOUT ); + } + } + + /* + * if we are looking for a specific msgid, check to see if it is + * associated with a dead connection and return an error if so. + */ + if ( msgid != LDAP_RES_ANY && msgid != LDAP_RES_UNSOLICITED ) { + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + if (( lr = nsldapi_find_request_by_msgid( ld, msgid )) + == NULL ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, + nsldapi_strdup( "unknown message id" )); + return( NSLDAPI_RESULT_ERROR ); /* could not find request for msgid */ + } + if ( lr->lr_conn != NULL && + lr->lr_conn->lconn_status == LDAP_CONNST_DEAD ) { + nsldapi_free_request( ld, lr, 1 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); + return( NSLDAPI_RESULT_ERROR ); /* connection dead */ + } + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + } + + if ( timeout == NULL ) { + tvp = NULL; + } else { + tv = *timeout; + tvp = &tv; + start_time = (long)time( NULL ); + } + + rc = NSLDAPI_RESULT_NOT_FOUND; + while ( rc == NSLDAPI_RESULT_NOT_FOUND ) { + msgfound = 0; +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + nsldapi_dump_connection( ld, ld->ld_conns, 1 ); + nsldapi_dump_requests_and_responses( ld ); + } +#endif /* LDAP_DEBUG */ + + /* + * Check if we have some data in a connection's BER buffer. + * If so, use it. + */ + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + if ( lc->lconn_sb->sb_ber.ber_ptr < + lc->lconn_sb->sb_ber.ber_end ) { + /* read1msg() might free the connection. */ + rc = read1msg( ld, msgid, all, lc->lconn_sb, + &lc, result ); + /* Indicate to next segment that we've processed a message + (or several, via chased refs) this time around. */ + msgfound = 1; + break; + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + + if ( !msgfound ) { + /* + * There was no buffered data. Poll to check connection + * status (read/write readiness). + */ + err = nsldapi_iostatus_poll( ld, tvp ); + +#if defined( LDAP_DEBUG ) && !defined( macintosh ) && !defined( DOS ) + if ( err == -1 ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_iostatus_poll returned -1: errno %d\n", + LDAP_GET_ERRNO( ld ), 0, 0 ); + } +#endif + +#if !defined( macintosh ) && !defined( DOS ) + /* + * If the restart option is enabled and the error + * was EINTR, try again. + */ + if ( err == -1 + && 0 != ( ld->ld_options & LDAP_BITOPT_RESTART ) + && LDAP_GET_ERRNO( ld ) == EINTR ) { + continue; + } +#endif + + /* + * Handle timeouts (no activity) and fatal errors. + */ + if ( err == -1 || err == 0 ) { + LDAP_SET_LDERRNO( ld, (err == -1 ? + LDAP_SERVER_DOWN : LDAP_TIMEOUT), NULL, + NULL ); + if ( err == -1 ) { + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + nsldapi_connection_lost_nolock( ld, + NULL ); + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + rc = NSLDAPI_RESULT_ERROR; + } else { + rc = NSLDAPI_RESULT_TIMEOUT; + } + return( rc ); + } + + /* + * Check each connection for interesting activity. + */ + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + for ( lc = ld->ld_conns; + rc == NSLDAPI_RESULT_NOT_FOUND && lc != NULL; + lc = nextlc ) { + nextlc = lc->lconn_next; + + /* + * For connections that are in the CONNECTING + * state, check for write ready (which + * indicates that the connection completed) and + * transition to the CONNECTED state. + */ + if ( lc->lconn_status == LDAP_CONNST_CONNECTING + && nsldapi_iostatus_is_write_ready( ld, + lc->lconn_sb ) ) { + lc->lconn_status = LDAP_CONNST_CONNECTED; + LDAPDebug( LDAP_DEBUG_TRACE, + "wait4msg: connection 0x%p -" + " LDAP_CONNST_CONNECTING ->" + " LDAP_CONNST_CONNECTED\n", + lc, 0, 0 ); + } + + if ( lc->lconn_status + != LDAP_CONNST_CONNECTED ) { + continue; + } + + /* + * For connections that are CONNECTED, check + * for read ready (which indicates that data + * from server is available), and, for + * connections with associated requests that + * have not yet been sent, write ready (okay + * to send some data to the server). + */ + if ( nsldapi_iostatus_is_read_ready( ld, + lc->lconn_sb )) { + /* read1msg() might free the connection. */ + rc = read1msg( ld, msgid, all, + lc->lconn_sb, &lc, result ); + } + + /* + * Send pending requests if possible. If there is no lc then + * it was a child connection closed by read1msg(). + */ + if ( lc && lc->lconn_pending_requests > 0 + && nsldapi_iostatus_is_write_ready( ld, + lc->lconn_sb )) { + err = nsldapi_send_pending_requests_nolock( + ld, lc ); + if ( err == -1 && + rc == NSLDAPI_RESULT_NOT_FOUND ) { + rc = NSLDAPI_RESULT_ERROR; + } + } + + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + } + + /* + * It is possible that recursion occurred while chasing + * referrals and as a result the message we are looking + * for may have been placed on the response queue. Look + * for it there before continuing so we don't end up + * waiting on the network for a message that we already + * received! + */ + if ( rc == NSLDAPI_RESULT_NOT_FOUND && + check_response_queue( ld, msgid, all, 0, result ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); + rc = (*result)->lm_msgtype; + } + + /* + * honor the timeout if specified + */ + if ( rc == NSLDAPI_RESULT_NOT_FOUND && tvp != NULL ) { + tmp_time = (long)time( NULL ); + if (( tv.tv_sec -= ( tmp_time - start_time )) <= 0 ) { + rc = NSLDAPI_RESULT_TIMEOUT; + LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, + NULL ); + break; + } + + LDAPDebug( LDAP_DEBUG_TRACE, "wait4msg: %ld secs to go\n", + tv.tv_sec, 0, 0 ); + start_time = tmp_time; + } + } + + return( rc ); +} + + +#define NSLDAPI_REQUEST_COMPLETE( lr ) \ + ( (lr)->lr_outrefcnt <= 0 && \ + (lr)->lr_res_msgtype != LDAP_RES_SEARCH_ENTRY && \ + (lr)->lr_res_msgtype != LDAP_RES_SEARCH_REFERENCE ) + +/* + * read1msg() should be called with LDAP_CONN_LOCK and LDAP_REQ_LOCK locked. + * + * Return values: + * > 0: message received; value is the tag of the message. + * NSLDAPI_RESULT_TIMEOUT timeout exceeded. + * NSLDAPI_RESULT_ERROR fatal error occurred such as connection closed. + * NSLDAPI_RESULT_NOT_FOUND message not yet complete; keep waiting. + * + * The LDAPConn passed in my be freed by read1msg() if the reference count + * shows that it's no longer needed. + */ +static int +read1msg( LDAP *ld, int msgid, int all, Sockbuf *sb, LDAPConn **lcp, + LDAPMessage **result ) +{ + BerElement *ber; + LDAPMessage *new, *l, *prev, *chainprev, *tmp; + ber_int_t id; + ber_tag_t tag; + ber_len_t len; + int terrno, lderr, foundit = 0; + LDAPRequest *lr; + int rc, has_parent, message_can_be_returned; + int manufactured_result = 0; + LDAPConn *lc = *lcp; + + LDAPDebug( LDAP_DEBUG_TRACE, "read1msg\n", 0, 0, 0 ); + + message_can_be_returned = 1; /* the usual case... */ + + /* + * if we are not already in the midst of reading a message, allocate + * a ber that is associated with this connection + */ + if ( lc->lconn_ber == NULLBER && nsldapi_alloc_ber_with_options( ld, + &lc->lconn_ber ) != LDAP_SUCCESS ) { + return( NSLDAPI_RESULT_ERROR ); + } + + /* + * ber_get_next() doesn't set errno on EOF, so we pre-set it to + * zero to avoid getting tricked by leftover "EAGAIN" errors + */ + LDAP_SET_ERRNO( ld, 0 ); + + /* get the next message */ + if ( (tag = ber_get_next( sb, &len, lc->lconn_ber )) + != LDAP_TAG_MESSAGE ) { + terrno = LDAP_GET_ERRNO( ld ); + if ( terrno == EWOULDBLOCK || terrno == EAGAIN ) { + return( NSLDAPI_RESULT_NOT_FOUND ); /* try again */ + } + LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN : + LDAP_LOCAL_ERROR), NULL, NULL ); + if ( tag == LBER_DEFAULT ) { + nsldapi_connection_lost_nolock( ld, sb ); + } + return( NSLDAPI_RESULT_ERROR ); + } + + /* + * Since we have received a complete message now, we pull this ber + * out of the connection structure and never read into it again. + */ + ber = lc->lconn_ber; + lc->lconn_ber = NULLBER; + + /* message id */ + if ( ber_get_int( ber, &id ) == LBER_ERROR ) { + ber_free( ber, 1 ); + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( NSLDAPI_RESULT_ERROR ); + } + + /* if it's been abandoned, toss it */ + if ( ldap_abandoned( ld, (int)id ) ) { + ber_free( ber, 1 ); + return( NSLDAPI_RESULT_NOT_FOUND ); /* continue looking */ + } + + if ( id == LDAP_RES_UNSOLICITED ) { + lr = NULL; + } else if (( lr = nsldapi_find_request_by_msgid( ld, id )) == NULL ) { + LDAPDebug( LDAP_DEBUG_ANY, + "no request for response with msgid %d (tossing)\n", + id, 0, 0 ); + ber_free( ber, 1 ); + return( NSLDAPI_RESULT_NOT_FOUND ); /* continue looking */ + } + + /* the message type */ + if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) { + ber_free( ber, 1 ); + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( NSLDAPI_RESULT_ERROR ); + } + LDAPDebug( LDAP_DEBUG_TRACE, "got %s msgid %d, original id %d\n", + ( tag == LDAP_RES_SEARCH_ENTRY ) ? "ENTRY" : + ( tag == LDAP_RES_SEARCH_REFERENCE ) ? "REFERENCE" : "RESULT", id, + ( lr == NULL ) ? id : lr->lr_origid ); + + if ( lr != NULL ) { + id = lr->lr_origid; + lr->lr_res_msgtype = tag; + } + rc = NSLDAPI_RESULT_NOT_FOUND; /* default is to keep looking (no response found) */ + + if ( id != LDAP_RES_UNSOLICITED && ( tag == LDAP_RES_SEARCH_REFERENCE || + tag != LDAP_RES_SEARCH_ENTRY )) { + int refchasing, reftotal, simple_request = 0; + LDAPControl **ctrls = NULL; + + check_for_refs( ld, lr, ber, lc->lconn_version, &reftotal, + &refchasing ); + + if ( refchasing > 0 || lr->lr_outrefcnt > 0 ) { + /* + * we're chasing one or more new refs... + */ + ber_free( ber, 1 ); + ber = NULLBER; + lr->lr_status = LDAP_REQST_CHASINGREFS; + message_can_be_returned = 0; + + } else if ( tag != LDAP_RES_SEARCH_REFERENCE ) { + /* + * this request is complete... + */ + has_parent = ( lr->lr_parent != NULL ); + + if ( lr->lr_outrefcnt <= 0 && !has_parent ) { + /* request without any refs */ + simple_request = ( reftotal == 0 ); + } + + /* + * If this is not a child request and it is a bind + * request, reset the connection's bind DN and + * status based on the result of the operation. + */ + if ( !has_parent && + LDAP_RES_BIND == lr->lr_res_msgtype && + lr->lr_conn != NULL ) { + if ( lr->lr_conn->lconn_binddn != NULL ) { + NSLDAPI_FREE( + lr->lr_conn->lconn_binddn ); + } + if ( LDAP_SUCCESS == nsldapi_parse_result( ld, + lr->lr_res_msgtype, ber, &lderr, NULL, + NULL, NULL, NULL ) + && LDAP_SUCCESS == lderr ) { + lr->lr_conn->lconn_bound = 1; + lr->lr_conn->lconn_binddn = + lr->lr_binddn; + lr->lr_binddn = NULL; + } else { + lr->lr_conn->lconn_bound = 0; + lr->lr_conn->lconn_binddn = NULL; + } + } + + /* + * if this response is to a child request, we toss + * the message contents and just merge error info. + * into the parent. + */ + if ( has_parent ) { + /* Extract any response controls from ber before ditching it */ + if( nsldapi_find_controls( ber, &ctrls ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + return( NSLDAPI_RESULT_ERROR ); + } + + ber_free( ber, 1 ); + ber = NULLBER; + } + while ( lr->lr_parent != NULL ) { + merge_error_info( ld, lr->lr_parent, lr ); + + lr = lr->lr_parent; + --lr->lr_outrefcnt; + if ( !NSLDAPI_REQUEST_COMPLETE(lr)) { + break; + } + } + /* Stash response controls in original request so they can be baked + into the manufactured result message later */ + if( ctrls != NULL ) { + if( lr->lr_res_ctrls != NULL ) { + /* There are controls saved in original request already, + replace them with the new ones because we only save + the final controls received. + May want to arrange for merging intermediate response + controls into the array in the future */ + ldap_controls_free( lr->lr_res_ctrls ); + } + lr->lr_res_ctrls = ctrls; + } + + /* + * we recognize a request as fully complete when: + * 1) it is not a child request (NULL parent) + * 2) it has no outstanding referrals + * 3) we have received a result for the request (i.e., + * something other than an entry or a reference). + */ + if ( lr->lr_parent == NULL + && NSLDAPI_REQUEST_COMPLETE(lr)) { + id = lr->lr_msgid; + tag = lr->lr_res_msgtype; + LDAPDebug( LDAP_DEBUG_TRACE, + "request %d done\n", id, 0, 0 ); +LDAPDebug( LDAP_DEBUG_TRACE, +"res_errno: %d, res_error: <%s>, res_matched: <%s>\n", +lr->lr_res_errno, lr->lr_res_error ? lr->lr_res_error : "", +lr->lr_res_matched ? lr->lr_res_matched : "" ); + if ( !simple_request ) { + if ( ber != NULLBER ) { + ber_free( ber, 1 ); + ber = NULLBER; + } + if ( build_result_ber( ld, &ber, lr ) + != LDAP_SUCCESS ) { + rc = NSLDAPI_RESULT_ERROR; + } else { + manufactured_result = 1; + } + } + + nsldapi_free_request( ld, lr, 1 ); + /* Since we asked nsldapi_free_request() to free the + connection, lets make sure our callers know it's gone. */ + *lcp = NULL; + } else { + message_can_be_returned = 0; + } + } + } + + if ( ber == NULLBER ) { + return( rc ); + } + + /* make a new ldap message */ + if ( (new = (LDAPMessage*)NSLDAPI_CALLOC( 1, sizeof(struct ldapmsg) )) + == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( NSLDAPI_RESULT_ERROR ); + } + new->lm_msgid = (int)id; + new->lm_msgtype = tag; + new->lm_ber = ber; + + /* + * if this is a search entry or if this request is complete (i.e., + * there are no outstanding referrals) then add to cache and check + * to see if we should return this to the caller right away or not. + */ + if ( message_can_be_returned ) { + if ( ld->ld_cache_on ) { + nsldapi_add_result_to_cache( ld, new ); + } + + if ( msgid == LDAP_RES_ANY || id == msgid ) { + if ( new->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { + /* + * return the first response we have for this + * search request later (possibly an entire + * chain of messages). + */ + foundit = 1; + } else if ( all == 0 + || (new->lm_msgtype != LDAP_RES_SEARCH_REFERENCE + && new->lm_msgtype != LDAP_RES_SEARCH_ENTRY) ) { + *result = new; + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, + NULL ); + return( tag ); + } + } + } + + /* + * if not, we must add it to the list of responses. if + * the msgid is already there, it must be part of an existing + * search response. + */ + + prev = NULL; + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) { + if ( l->lm_msgid == new->lm_msgid ) + break; + prev = l; + } + + /* not part of an existing search response */ + if ( l == NULL ) { + if ( foundit ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + *result = new; + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); + return( tag ); + } + + new->lm_next = ld->ld_responses; + ld->ld_responses = new; + LDAPDebug( LDAP_DEBUG_TRACE, + "adding new response id %d type %d (looking for id %d)\n", + new->lm_msgid, new->lm_msgtype, msgid ); + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + if( message_can_be_returned ) + POST( ld, new->lm_msgid, new ); + return( NSLDAPI_RESULT_NOT_FOUND ); /* continue looking */ + } + + LDAPDebug( LDAP_DEBUG_TRACE, + "adding response 0x%p - id %d type %d", + new, new->lm_msgid, new->lm_msgtype ); + LDAPDebug( LDAP_DEBUG_TRACE, " (looking for id %d)\n", msgid, 0, 0 ); + + /* + * part of a search response - add to end of list of entries + * + * the first step is to find the end of the list of entries and + * references. after the following loop is executed, tmp points to + * the last entry or reference in the chain. If there are none, + * tmp points to the search result. + */ + chainprev = NULL; + for ( tmp = l; tmp->lm_chain != NULL && + ( tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY + || tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ); + tmp = tmp->lm_chain ) { + chainprev = tmp; + } + + /* + * If this is a manufactured result message and a result is already + * queued we throw away the one that is queued and replace it with + * our new result. This is necessary so we don't end up returning + * more than one result. + */ + if ( manufactured_result && + tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { + /* + * the result is the only thing in the chain... replace it. + */ + new->lm_chain = tmp->lm_chain; + new->lm_next = tmp->lm_next; + if ( chainprev == NULL ) { + if ( prev == NULL ) { + ld->ld_responses = new; + } else { + prev->lm_next = new; + } + } else { + chainprev->lm_chain = new; + } + if ( l == tmp ) { + l = new; + } + ldap_msgfree( tmp ); + + } else if ( manufactured_result && tmp->lm_chain != NULL + && tmp->lm_chain->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { + /* + * entries or references are also present, so the result + * is the next entry after tmp. replace it. + */ + new->lm_chain = tmp->lm_chain->lm_chain; + new->lm_next = tmp->lm_chain->lm_next; + ldap_msgfree( tmp->lm_chain ); + tmp->lm_chain = new; + + } else if ( tmp->lm_msgtype == LDAP_RES_SEARCH_RESULT ) { + /* + * the result is the only thing in the chain... add before it. + */ + new->lm_chain = tmp; + if ( chainprev == NULL ) { + if ( prev == NULL ) { + ld->ld_responses = new; + } else { + prev->lm_next = new; + } + } else { + chainprev->lm_chain = new; + } + if ( l == tmp ) { + l = new; + } + + } else { + /* + * entries and/or references are present... add to the end + * of the entry/reference part of the chain. + */ + new->lm_chain = tmp->lm_chain; + tmp->lm_chain = new; + } + + /* + * return the first response or the whole chain if that's what + * we were looking for.... + */ + if ( foundit ) { + if ( all == 0 && l->lm_chain != NULL ) { + /* + * only return the first response in the chain + */ + if ( prev == NULL ) { + ld->ld_responses = l->lm_chain; + } else { + prev->lm_next = l->lm_chain; + } + l->lm_chain = NULL; + tag = l->lm_msgtype; + } else { + /* + * return all of the responses (may be a chain) + */ + if ( prev == NULL ) { + ld->ld_responses = l->lm_next; + } else { + prev->lm_next = l->lm_next; + } + } + *result = l; + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); + return( tag ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + return( NSLDAPI_RESULT_NOT_FOUND ); /* continue looking */ +} + + +/* + * check for LDAPv2+ (UMich extension) or LDAPv3 referrals or references + * errors are merged in "lr". + */ +static void +check_for_refs( LDAP *ld, LDAPRequest *lr, BerElement *ber, + int ldapversion, int *totalcountp, int *chasingcountp ) +{ + int err, origerr; + char *errstr, *matcheddn, **v3refs; + + LDAPDebug( LDAP_DEBUG_TRACE, "check_for_refs\n", 0, 0, 0 ); + + *chasingcountp = *totalcountp = 0; + + if ( ldapversion < LDAP_VERSION2 || ( lr->lr_parent == NULL + && ( ld->ld_options & LDAP_BITOPT_REFERRALS ) == 0 )) { + /* referrals are not supported or are disabled */ + return; + } + + if ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + err = nsldapi_parse_reference( ld, ber, &v3refs, NULL ); + origerr = LDAP_REFERRAL; /* a small lie... */ + matcheddn = errstr = NULL; + } else { + err = nsldapi_parse_result( ld, lr->lr_res_msgtype, ber, + &origerr, &matcheddn, &errstr, &v3refs, NULL ); + } + + if ( err != LDAP_SUCCESS ) { + /* parse failed */ + return; + } + + if ( origerr == LDAP_REFERRAL ) { /* ldapv3 */ + if ( v3refs != NULL ) { + err = nsldapi_chase_v3_refs( ld, lr, v3refs, + ( lr->lr_res_msgtype == LDAP_RES_SEARCH_REFERENCE ), + totalcountp, chasingcountp ); + ldap_value_free( v3refs ); + } + } else if ( ldapversion == LDAP_VERSION2 + && origerr != LDAP_SUCCESS ) { + /* referrals may be present in the error string */ + err = nsldapi_chase_v2_referrals( ld, lr, &errstr, + totalcountp, chasingcountp ); + } + + /* set LDAP errno, message, and matched string appropriately */ + if ( lr->lr_res_error != NULL ) { + NSLDAPI_FREE( lr->lr_res_error ); + } + lr->lr_res_error = errstr; + + if ( lr->lr_res_matched != NULL ) { + NSLDAPI_FREE( lr->lr_res_matched ); + } + lr->lr_res_matched = matcheddn; + + if ( err == LDAP_SUCCESS && ( *chasingcountp == *totalcountp )) { + if ( *totalcountp > 0 && ( origerr == LDAP_PARTIAL_RESULTS + || origerr == LDAP_REFERRAL )) { + /* substitute success for referral error codes */ + lr->lr_res_errno = LDAP_SUCCESS; + } else { + /* preserve existing non-referral error code */ + lr->lr_res_errno = origerr; + } + } else if ( err != LDAP_SUCCESS ) { + /* error occurred while trying to chase referrals */ + lr->lr_res_errno = err; + } else { + /* some referrals were not recognized */ + lr->lr_res_errno = ( ldapversion == LDAP_VERSION2 ) + ? LDAP_PARTIAL_RESULTS : LDAP_REFERRAL; + } + + LDAPDebug( LDAP_DEBUG_TRACE, + "check_for_refs: new result: msgid %d, res_errno %d, ", + lr->lr_msgid, lr->lr_res_errno, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, " res_error <%s>, res_matched <%s>\n", + lr->lr_res_error ? lr->lr_res_error : "", + lr->lr_res_matched ? lr->lr_res_matched : "", 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, + "check_for_refs: %d new refs(s); chasing %d of them\n", + *totalcountp, *chasingcountp, 0 ); +} + + +/* returns an LDAP error code and also sets it in LDAP * */ +static int +build_result_ber( LDAP *ld, BerElement **berp, LDAPRequest *lr ) +{ + ber_len_t len; + ber_int_t along; + BerElement *ber; + int err; + + if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( err ); + } + *berp = ber; + if ( ber_printf( ber, lr->lr_res_ctrls ? "{it{ess}" : "{it{ess}}", + lr->lr_msgid, (long)lr->lr_res_msgtype, lr->lr_res_errno, + lr->lr_res_matched ? lr->lr_res_matched : "", + lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) { + return( LDAP_ENCODING_ERROR ); + } + + if ( NULL != lr->lr_res_ctrls && nsldapi_put_controls( ld, + lr->lr_res_ctrls, 1 /* close seq */, ber ) != LDAP_SUCCESS ) { + return( LDAP_ENCODING_ERROR ); + } + + ber_reset( ber, 1 ); + if ( ber_skip_tag( ber, &len ) == LBER_ERROR || + ber_get_int( ber, &along ) == LBER_ERROR || + ber_peek_tag( ber, &len ) == LBER_ERROR ) { + return( LDAP_DECODING_ERROR ); + } + + return( LDAP_SUCCESS ); +} + + +static void +merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ) +{ +/* + * Merge error information in "lr" with "parentr" error code and string. + */ + if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) { + parentr->lr_res_errno = lr->lr_res_errno; + if ( lr->lr_res_error != NULL ) { + (void)nsldapi_append_referral( ld, &parentr->lr_res_error, + lr->lr_res_error ); + } + } else if ( lr->lr_res_errno != LDAP_SUCCESS && + parentr->lr_res_errno == LDAP_SUCCESS ) { + parentr->lr_res_errno = lr->lr_res_errno; + if ( parentr->lr_res_error != NULL ) { + NSLDAPI_FREE( parentr->lr_res_error ); + } + parentr->lr_res_error = lr->lr_res_error; + lr->lr_res_error = NULL; + if ( NAME_ERROR( lr->lr_res_errno )) { + if ( parentr->lr_res_matched != NULL ) { + NSLDAPI_FREE( parentr->lr_res_matched ); + } + parentr->lr_res_matched = lr->lr_res_matched; + lr->lr_res_matched = NULL; + } + } + + LDAPDebug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ", + parentr->lr_msgid, 0, 0 ); + LDAPDebug( LDAP_DEBUG_TRACE, "result lderrno %d, error <%s>, matched <%s>\n", + parentr->lr_res_errno, parentr->lr_res_error ? + parentr->lr_res_error : "", parentr->lr_res_matched ? + parentr->lr_res_matched : "" ); +} + +#if defined( CLDAP ) +#if !defined( macintosh ) && !defined( DOS ) && !defined( _WINDOWS ) && !defined(XP_OS2) +/* XXXmcs: was revised to support extended I/O callbacks but never compiled! */ +static int +cldap_select1( LDAP *ld, struct timeval *timeout ) +{ + int rc; + static int tblsize = 0; + NSLDAPIIOStatus *iosp = ld->ld_iostatus; + + if ( tblsize == 0 ) { +#ifdef USE_SYSCONF + tblsize = sysconf( _SC_OPEN_MAX ); +#else /* USE_SYSCONF */ + tblsize = getdtablesize(); +#endif /* USE_SYSCONF */ + } + + if ( tblsize >= FD_SETSIZE ) { + /* + * clamp value so we don't overrun the fd_set structure + */ + tblsize = FD_SETSIZE - 1; + } + + if ( NSLDAPI_IOSTATUS_TYPE_OSNATIVE == iosp->ios_type ) { + fd_set readfds; + + FD_ZERO( &readfds ); + FD_SET( ld->ld_sbp->sb_sd, &readfds ); + + /* XXXmcs: UNIX platforms should use poll() */ + rc = select( tblsize, &readfds, 0, 0, timeout ) ); + + } else if ( NSLDAPI_IOSTATUS_TYPE_CALLBACK == iosp->ios_type ) { + LDAP_X_PollFD pollfds[ 1 ]; + + pollfds[0].lpoll_fd = ld->ld_sbp->sb_sd; + pollfds[0].lpoll_arg = ld->ld_sbp->sb_arg; + pollfds[0].lpoll_events = LDAP_X_POLLIN; + pollfds[0].lpoll_revents = 0; + rc = ld->ld_extpoll_fn( pollfds, 1, nsldapi_tv2ms( timeout ), + ld->ld_ext_session_arg ); + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_poll: unknown I/O type %d\n", + rc = 0; /* simulate a timeout (what else to do?) */ + } + + return( rc ); +} +#endif /* !macintosh */ + + +#ifdef macintosh +static int +cldap_select1( LDAP *ld, struct timeval *timeout ) +{ + /* XXXmcs: needs to be revised to support I/O callbacks */ + return( tcpselect( ld->ld_sbp->sb_sd, timeout )); +} +#endif /* macintosh */ + + +#if (defined( DOS ) && defined( WINSOCK )) || defined( _WINDOWS ) || defined(XP_OS2) +/* XXXmcs: needs to be revised to support extended I/O callbacks */ +static int +cldap_select1( LDAP *ld, struct timeval *timeout ) +{ + fd_set readfds; + int rc; + + FD_ZERO( &readfds ); + FD_SET( ld->ld_sbp->sb_sd, &readfds ); + + if ( NSLDAPI_IO_TYPE_STANDARD == ld->ldiou_type && + NULL != ld->ld_select_fn ) { + rc = ld->ld_select_fn( 1, &readfds, 0, 0, timeout ); + } else if ( NSLDAPI_IO_TYPE_EXTENDED == ld->ldiou_type && + NULL != ld->ld_extselect_fn ) { + rc = ld->ld_extselect_fn( ld->ld_ext_session_arg, 1, &readfds, 0, + 0, timeout ) ); + } else { + /* XXXmcs: UNIX platforms should use poll() */ + rc = select( 1, &readfds, 0, 0, timeout ) ); + } + + return( rc == SOCKET_ERROR ? -1 : rc ); +} +#endif /* WINSOCK || _WINDOWS */ +#endif /* CLDAP */ + +int +LDAP_CALL +ldap_msgfree( LDAPMessage *lm ) +{ + LDAPMessage *next; + int type = 0; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 ); + + for ( ; lm != NULL; lm = next ) { + next = lm->lm_chain; + type = lm->lm_msgtype; + ber_free( lm->lm_ber, 1 ); + NSLDAPI_FREE( (char *) lm ); + } + + return( type ); +} + +/* + * ldap_msgdelete - delete a message. It returns: + * 0 if the entire message was deleted + * -1 if the message was not found, or only part of it was found + */ +int +ldap_msgdelete( LDAP *ld, int msgid ) +{ + LDAPMessage *lm, *prev; + int msgtype; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_msgdelete\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); /* punt */ + } + + prev = NULL; + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) { + if ( lm->lm_msgid == msgid ) + break; + prev = lm; + } + + if ( lm == NULL ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + return( -1 ); + } + + if ( prev == NULL ) + ld->ld_responses = lm->lm_next; + else + prev->lm_next = lm->lm_next; + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + + msgtype = ldap_msgfree( lm ); + if ( msgtype == LDAP_RES_SEARCH_ENTRY + || msgtype == LDAP_RES_SEARCH_REFERENCE ) { + return( -1 ); + } + + return( 0 ); +} + + +/* + * return 1 if message msgid is waiting to be abandoned, 0 otherwise + */ +static int +ldap_abandoned( LDAP *ld, int msgid ) +{ + int i; + + LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK ); + if ( ld->ld_abandoned == NULL ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( 0 ); + } + + for ( i = 0; ld->ld_abandoned[i] != -1; i++ ) + if ( ld->ld_abandoned[i] == msgid ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( 1 ); + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( 0 ); +} + + +static int +ldap_mark_abandoned( LDAP *ld, int msgid ) +{ + int i; + + LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK ); + if ( ld->ld_abandoned == NULL ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( -1 ); + } + + for ( i = 0; ld->ld_abandoned[i] != -1; i++ ) + if ( ld->ld_abandoned[i] == msgid ) + break; + + if ( ld->ld_abandoned[i] == -1 ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( -1 ); + } + + for ( ; ld->ld_abandoned[i] != -1; i++ ) { + ld->ld_abandoned[i] = ld->ld_abandoned[i + 1]; + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK ); + return( 0 ); +} + + +#ifdef CLDAP +int +cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement **ber ) +{ + int rc; + ber_tag_t tag; + ber_len_t len; + + if ( ld->ld_sbp->sb_ber.ber_ptr >= ld->ld_sbp->sb_ber.ber_end ) { + rc = cldap_select1( ld, timeout ); + if ( rc == -1 || rc == 0 ) { + LDAP_SET_LDERRNO( ld, (rc == -1 ? LDAP_SERVER_DOWN : + LDAP_TIMEOUT), NULL, NULL ); + return( rc ); + } + } + + /* get the next message */ + if ( (tag = ber_get_next( ld->ld_sbp, &len, ber )) + != LDAP_TAG_MESSAGE ) { + LDAP_SET_LDERRNO( ld, (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN : + LDAP_LOCAL_ERROR), NULL, NULL ); + return( -1 ); + } + + return( tag ); +} +#endif /* CLDAP */ + +int +nsldapi_post_result( LDAP *ld, int msgid, LDAPMessage *result ) +{ + LDAPPend *lp; + + LDAPDebug( LDAP_DEBUG_TRACE, + "nsldapi_post_result(ld=0x%p, msgid=%d, result=0x%p)\n", + ld, msgid, result ); + LDAP_MUTEX_LOCK( ld, LDAP_PEND_LOCK ); + if( msgid == LDAP_RES_ANY ) { + /* + * Look for any pending request for which someone is waiting. + */ + for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next ) + { + if ( lp->lp_sema != NULL ) { + break; + } + } + /* + * If we did't find a pending request, lp is NULL at this + * point, and we will leave this function without doing + * anything more -- which is exactly what we want to do. + */ + } + else + { + /* + * Look for a pending request specific to this message id + */ + for( lp = ld->ld_pend; lp != NULL; lp = lp->lp_next ) + { + if( lp->lp_msgid == msgid ) + break; + } + + if( lp == NULL ) + { + /* + * No pending requests for this response... append to + * our pending result list. + */ + LDAPPend *newlp; + newlp = (LDAPPend *)NSLDAPI_CALLOC( 1, + sizeof( LDAPPend )); + if( newlp == NULL ) + { + LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK ); + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, + NULL ); + return (-1); + } + newlp->lp_msgid = msgid; + newlp->lp_result = result; + link_pend( ld, newlp ); + } + } + + + if( lp != NULL ) + { + /* + * Wake up a thread that is waiting for this result. + */ + lp->lp_msgid = msgid; + lp->lp_result = result; + LDAP_SEMA_POST( ld, lp ); + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_PEND_LOCK ); + return (0); +} + +static void +link_pend( LDAP *ld, LDAPPend *lp ) +{ + if (( lp->lp_next = ld->ld_pend ) != NULL ) + { + lp->lp_next->lp_prev = lp; + } + ld->ld_pend = lp; + lp->lp_prev = NULL; +} diff --git a/ldap/c-sdk/libraries/libldap/saslbind.c b/ldap/c-sdk/libraries/libldap/saslbind.c new file mode 100644 index 0000000000..5cbe73bbff --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/saslbind.c @@ -0,0 +1,877 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +#ifdef LDAP_SASLIO_HOOKS +/* + * Global SASL Init data + */ + +static int +nsldapi_sasl_fail() +{ + return( SASL_FAIL ); +} + +sasl_callback_t client_callbacks[] = { + { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL }, + { SASL_CB_GETREALM, NULL, NULL }, + { SASL_CB_USER, NULL, NULL }, + { SASL_CB_CANON_USER, NULL, NULL }, + { SASL_CB_AUTHNAME, NULL, NULL }, + { SASL_CB_PASS, NULL, NULL }, + { SASL_CB_ECHOPROMPT, NULL, NULL }, + { SASL_CB_NOECHOPROMPT, NULL, NULL }, + { SASL_CB_LIST_END, NULL, NULL } +}; + +int +nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg ) +{ + int rc = LDAP_LOCAL_ERROR; + + switch (err) { + case SASL_OK: + rc = LDAP_SUCCESS; + break; + case SASL_NOMECH: + rc = LDAP_AUTH_UNKNOWN; + break; + case SASL_BADSERV: + rc = LDAP_CONNECT_ERROR; + break; + case SASL_DISABLED: + case SASL_ENCRYPT: + case SASL_EXPIRED: + case SASL_NOUSERPASS: + case SASL_NOVERIFY: + case SASL_PWLOCK: + case SASL_TOOWEAK: + case SASL_UNAVAIL: + case SASL_WEAKPASS: + rc = LDAP_INAPPROPRIATE_AUTH; + break; + case SASL_BADAUTH: + case SASL_NOAUTHZ: + rc = LDAP_INVALID_CREDENTIALS; + break; + case SASL_NOMEM: + rc = LDAP_NO_MEMORY; + break; + case SASL_NOUSER: + rc = LDAP_NO_SUCH_OBJECT; + break; + case SASL_CONTINUE: + case SASL_FAIL: + case SASL_INTERACT: + default: + rc = LDAP_LOCAL_ERROR; + break; + } + + LDAP_SET_LDERRNO( ld, rc, NULL, msg ); + return( rc ); +} + +#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER +/* + * Get available SASL Mechanisms supported by the server + */ + +static int +nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech ) +{ + char *attr[] = { "supportedSASLMechanisms", NULL }; + char **values, **v, *mech, *m; + LDAPMessage *res, *e; + struct timeval timeout; + int slen, rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + timeout.tv_sec = SEARCH_TIMEOUT_SECS; + timeout.tv_usec = 0; + + rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE, + "objectclass=*", attr, 0, &timeout, &res ); + + if ( rc != LDAP_SUCCESS ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + e = ldap_first_entry( ld, res ); + if ( e == NULL ) { + ldap_msgfree( res ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL ); + } + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); + if ( values == NULL ) { + ldap_msgfree( res ); + LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL ); + return( LDAP_NO_SUCH_ATTRIBUTE ); + } + + slen = 0; + for(v = values; *v != NULL; v++ ) { + slen += strlen(*v) + 1; + } + if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) { + ldap_value_free( values ); + ldap_msgfree( res ); + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + m = mech; + for(v = values; *v; v++) { + if (v != values) { + *m++ = ' '; + } + slen = strlen(*v); + strncpy(m, *v, slen); + m += slen; + } + *m = '\0'; + + ldap_value_free( values ); + ldap_msgfree( res ); + + *pmech = mech; + + return( LDAP_SUCCESS ); +} +#endif /* LDAP_SASLIO_GET_MECHS_FROM_SERVER */ + +int +nsldapi_sasl_secprops( + const char *in, + sasl_security_properties_t *secprops ) +{ + int i; + char **props = NULL; + char *inp; + unsigned sflags = 0; + sasl_ssf_t max_ssf = 0; + sasl_ssf_t min_ssf = 0; + unsigned maxbufsize = 0; + int got_sflags = 0; + int got_max_ssf = 0; + int got_min_ssf = 0; + int got_maxbufsize = 0; + + if (in == NULL) { + return LDAP_PARAM_ERROR; + } + inp = nsldapi_strdup(in); + if (inp == NULL) { + return LDAP_PARAM_ERROR; + } + props = ldap_str2charray( inp, "," ); + NSLDAPI_FREE( inp ); + + if( props == NULL || secprops == NULL ) { + return LDAP_PARAM_ERROR; + } + + for( i=0; props[i]; i++ ) { + if( strcasecmp(props[i], "none") == 0 ) { + got_sflags++; + + } else if( strcasecmp(props[i], "noactive") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_NOACTIVE; + + } else if( strcasecmp(props[i], "noanonymous") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_NOANONYMOUS; + + } else if( strcasecmp(props[i], "nodict") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_NODICTIONARY; + + } else if( strcasecmp(props[i], "noplain") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_NOPLAINTEXT; + + } else if( strcasecmp(props[i], "forwardsec") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_FORWARD_SECRECY; + + } else if( strcasecmp(props[i], "passcred") == 0 ) { + got_sflags++; + sflags |= SASL_SEC_PASS_CREDENTIALS; + + } else if( strncasecmp(props[i], + "minssf=", sizeof("minssf")) == 0 ) { + if( isdigit( props[i][sizeof("minssf")] ) ) { + got_min_ssf++; + min_ssf = atoi( &props[i][sizeof("minssf")] ); + } else { + return LDAP_NOT_SUPPORTED; + } + + } else if( strncasecmp(props[i], + "maxssf=", sizeof("maxssf")) == 0 ) { + if( isdigit( props[i][sizeof("maxssf")] ) ) { + got_max_ssf++; + max_ssf = atoi( &props[i][sizeof("maxssf")] ); + } else { + return LDAP_NOT_SUPPORTED; + } + + } else if( strncasecmp(props[i], + "maxbufsize=", sizeof("maxbufsize")) == 0 ) { + if( isdigit( props[i][sizeof("maxbufsize")] ) ) { + got_maxbufsize++; + maxbufsize = atoi( &props[i][sizeof("maxbufsize")] ); + if( maxbufsize && + (( maxbufsize < SASL_MIN_BUFF_SIZE ) + || (maxbufsize > SASL_MAX_BUFF_SIZE ))) { + return( LDAP_PARAM_ERROR ); + } + } else { + return( LDAP_NOT_SUPPORTED ); + } + } else { + return( LDAP_NOT_SUPPORTED ); + } + } + + if(got_sflags) { + secprops->security_flags = sflags; + } + if(got_min_ssf) { + secprops->min_ssf = min_ssf; + } + if(got_max_ssf) { + secprops->max_ssf = max_ssf; + } + if(got_maxbufsize) { + secprops->maxbufsize = maxbufsize; + } + + ldap_charray_free( props ); + return( LDAP_SUCCESS ); +} +#endif /* LDAP_SASLIO_HOOKS */ + +static int +nsldapi_sasl_bind_s( + LDAP *ld, + const char *dn, + const char *mechanism, + const struct berval *cred, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct berval **servercredp, + LDAPControl ***responsectrls +) +{ + int err, msgid; + LDAPMessage *result; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_sasl_bind_s\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + if ( ( err = ldap_sasl_bind( ld, dn, mechanism, cred, serverctrls, + clientctrls, &msgid )) != LDAP_SUCCESS ) + return( err ); + + if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + /* Get the controls sent by the server if requested */ + if ( responsectrls ) { + if ( ( err = ldap_parse_result( ld, result, &err, NULL, NULL, + NULL, responsectrls, 0 )) != LDAP_SUCCESS ) + return( err ); + } + + err = ldap_parse_sasl_bind_result( ld, result, servercredp, 0 ); + if (err != LDAP_SUCCESS && err != LDAP_SASL_BIND_IN_PROGRESS) { + ldap_msgfree( result ); + return( err ); + } + + return( ldap_result2error( ld, result, 1 ) ); +} + +#ifdef LDAP_SASLIO_HOOKS +static int +nsldapi_sasl_do_bind( LDAP *ld, const char *dn, + const char *mechs, unsigned flags, + LDAP_SASL_INTERACT_PROC *callback, void *defaults, + LDAPControl **sctrl, LDAPControl **cctrl, LDAPControl ***rctrl ) +{ + sasl_interact_t *prompts = NULL; + sasl_conn_t *ctx = NULL; + sasl_ssf_t *ssf = NULL; + const char *mech = NULL; + int saslrc, rc; + struct berval ccred; + unsigned credlen; + int stepnum = 1; + char *sasl_username = NULL; + + if (rctrl) { + /* init to NULL so we can call ldap_controls_free below */ + *rctrl = NULL; + } + + if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + /* shouldn't happen */ + if (callback == NULL) { + return( LDAP_LOCAL_ERROR ); + } + + if ( (rc = nsldapi_sasl_open(ld, NULL, &ctx, 0)) != LDAP_SUCCESS ) { + return( rc ); + } + + ccred.bv_val = NULL; + ccred.bv_len = 0; + + LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n", + (mechs ? mechs : ""), 0, 0 ); + + do { + saslrc = sasl_client_start( ctx, + mechs, + &prompts, + (const char **)&ccred.bv_val, + &credlen, + &mech ); + + LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n", + stepnum, (mech ? mech : ""), 0 ); + stepnum++; + + if( saslrc == SASL_INTERACT && + (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) { + break; + } + } while ( saslrc == SASL_INTERACT ); + + ccred.bv_len = credlen; + + if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { + return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); + } + + stepnum = 1; + + do { + struct berval *scred; + int clientstepnum = 1; + + scred = NULL; + + if (rctrl) { + /* if we're looping again, we need to free any controls set + during the previous loop */ + /* NOTE that this assumes we only care about the controls + returned by the last call to nsldapi_sasl_bind_s - if + we care about _all_ controls, we will have to figure out + some way to append them each loop go round */ + ldap_controls_free(*rctrl); + *rctrl = NULL; + } + + LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n", + stepnum, (mech ? mech : ""), 0 ); + stepnum++; + + /* notify server of a sasl bind step */ + rc = nsldapi_sasl_bind_s(ld, dn, mech, &ccred, + sctrl, cctrl, &scred, rctrl); + + if ( ccred.bv_val != NULL ) { + ccred.bv_val = NULL; + } + + if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { + ber_bvfree( scred ); + return( rc ); + } + + if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) { + /* we're done, no need to step */ + if( scred ) { + if ( scred->bv_len == 0 ) { /* MS AD sends back empty screds */ + LDAPDebug(LDAP_DEBUG_ANY, + "SASL BIND complete - ignoring empty credential response\n", + 0, 0, 0); + ber_bvfree( scred ); + } else { + /* but server provided us with data! */ + LDAPDebug(LDAP_DEBUG_TRACE, + "SASL BIND complete but invalid because server responded with credentials - length [%u]\n", + scred->bv_len, 0, 0); + ber_bvfree( scred ); + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, + NULL, "Error during SASL handshake - invalid server credential response" ); + return( LDAP_LOCAL_ERROR ); + } + } + break; + } + + /* perform the next step of the sasl bind */ + do { + LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n", + clientstepnum, stepnum, (mech ? mech : "") ); + clientstepnum++; + saslrc = sasl_client_step( ctx, + (scred == NULL) ? NULL : scred->bv_val, + (scred == NULL) ? 0 : scred->bv_len, + &prompts, + (const char **)&ccred.bv_val, + &credlen ); + + if( saslrc == SASL_INTERACT && + (callback)(ld, flags, defaults, prompts) + != LDAP_SUCCESS ) { + break; + } + } while ( saslrc == SASL_INTERACT ); + + ccred.bv_len = credlen; + ber_bvfree( scred ); + + if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { + return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); + } + } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + if ( saslrc != SASL_OK ) { + return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) ); + } + + saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username ); + if ( (saslrc == SASL_OK) && sasl_username ) { + LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0); + } + + saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf ); + if( saslrc == SASL_OK ) { + if( ssf && *ssf ) { + LDAPDebug(LDAP_DEBUG_TRACE, + "SASL install encryption, for SSF: %lu\n", + (unsigned long) *ssf, 0, 0 ); + nsldapi_sasl_install( ld, NULL ); + } + } + + return( rc ); +} +#endif /* LDAP_SASLIO_HOOKS */ + + +/* + * ldap_sasl_bind - authenticate to the ldap server. The dn, mechanism, + * and credentials of the entry to which to bind are supplied. An LDAP + * error code is returned and if LDAP_SUCCESS is returned *msgidp is set + * to the id of the request initiated. + * + * Example: + * struct berval creds; + * LDAPControl **ctrls; + * int err, msgid; + * ... fill in creds with credentials ... + * ... fill in ctrls with server controls ... + * err = ldap_sasl_bind( ld, "cn=manager, o=university of michigan, c=us", + * "mechanismname", &creds, ctrls, NULL, &msgid ); + */ +int +LDAP_CALL +ldap_sasl_bind( + LDAP *ld, + const char *dn, + const char *mechanism, + const struct berval *cred, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp +) +{ + BerElement *ber; + int rc, simple, msgid, ldapversion; + + /* + * The ldapv3 bind request looks like this: + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING, -- passwd + * sasl [3] SaslCredentials -- v3 only + * } + * } + * SaslCredentials ::= SEQUENCE { + * mechanism LDAPString, + * credentials OCTET STRING + * } + * all wrapped up in an LDAPMessage sequence. + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( msgidp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( ( ld->ld_options & LDAP_BITOPT_RECONNECT ) != 0 ) { + nsldapi_handle_reconnect( ld ); + } + + simple = ( mechanism == LDAP_SASL_SIMPLE ); + ldapversion = NSLDAPI_LDAP_VERSION( ld ); + + /* only ldapv3 or higher can do sasl binds */ + if ( !simple && ldapversion < LDAP_VERSION3 ) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + if ( dn == NULL ) + dn = ""; + + if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn, + cred, LDAP_AUTH_SASL )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* create a message to send */ + if (( rc = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( rc ); + } + + /* fill it in */ + if ( simple ) { /* simple bind; works in LDAPv2 or v3 */ + struct berval tmpcred; + + if ( cred == NULL ) { + tmpcred.bv_val = ""; + tmpcred.bv_len = 0; + cred = &tmpcred; + } + rc = ber_printf( ber, "{it{isto}", msgid, LDAP_REQ_BIND, + ldapversion, dn, LDAP_AUTH_SIMPLE, cred->bv_val, + cred->bv_len ); + + } else { /* SASL bind; requires LDAPv3 or better */ + if ( cred == NULL || cred->bv_val == NULL || cred->bv_len == 0) { + rc = ber_printf( ber, "{it{ist{s}}", msgid, + LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL, + mechanism ); + } else { + rc = ber_printf( ber, "{it{ist{so}}", msgid, + LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL, + mechanism, cred->bv_val, + cred->bv_len ); + } + } + + if ( rc == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if ( (rc = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( rc ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND, + (char *)dn, ber ); + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + +/* + * ldap_sasl_bind_s - bind to the ldap server using sasl authentication + * The dn, mechanism, and credentials of the entry to which to bind are + * supplied. LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + * + * Example: + * struct berval creds; + * ... fill in creds with credentials ... + * ldap_sasl_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "mechanismname", &creds ) + */ +int +LDAP_CALL +ldap_sasl_bind_s( + LDAP *ld, + const char *dn, + const char *mechanism, + const struct berval *cred, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct berval **servercredp +) +{ + return ( nsldapi_sasl_bind_s( ld, dn, mechanism, cred, + serverctrls, clientctrls, servercredp, NULL ) ); +} + +#ifdef LDAP_SASLIO_HOOKS +/* + * SASL Authentication Interface: ldap_sasl_interactive_bind_s + * + * This routine takes a DN, SASL mech list, and a SASL callback + * and performs the necessary sequencing to complete a SASL bind + * to the LDAP connection ld. The user provided callback can + * use an optionally provided set of default values to complete + * any necessary interactions. + * + * Currently imposes the following restrictions: + * A mech list must be provided + * LDAP_SASL_INTERACTIVE mode requires a callback + */ +int +LDAP_CALL +ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn, + const char *saslMechanism, + LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, + LDAP_SASL_INTERACT_PROC *callback, void *defaults ) +{ + return ldap_sasl_interactive_bind_ext_s( ld, dn, + saslMechanism, sctrl, cctrl, flags, callback, + defaults, NULL ); +} + +/* + * ldap_sasl_interactive_bind_ext_s + * + * This function extends ldap_sasl_interactive_bind_s by allowing + * controls received from the server to be returned as rctrl. The + * caller must pass in a valid LDAPControl** pointer and free the + * array of controls when finished with them e.g. + * LDAPControl **retctrls = NULL; + * ... + * ldap_sasl_interactive_bind_ext_s(ld, ...., &retctrls); + * ... + * ldap_controls_free(retctrls); + * Only the controls from the server during the last bind step are returned - + * intermediate controls (if any, usually not) are discarded. + */ +int +LDAP_CALL +ldap_sasl_interactive_bind_ext_s( LDAP *ld, const char *dn, + const char *saslMechanism, + LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, + LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl ***rctrl ) +{ +#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER + char *smechs; +#endif + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ((flags == LDAP_SASL_INTERACTIVE) && (callback == NULL)) { + return( LDAP_PARAM_ERROR ); + } + + LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK ); + + if( saslMechanism == NULL || *saslMechanism == '\0' ) { +#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER + rc = nsldapi_get_sasl_mechs( ld, &smechs ); + if( rc != LDAP_SUCCESS ) { + LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); + return( rc ); + } + saslMechanism = smechs; +#else + LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); + return( LDAP_PARAM_ERROR ); +#endif /* LDAP_SASLIO_GET_MECHS_FROM_SERVER */ + } + + rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism, + flags, callback, defaults, sctrl, cctrl, rctrl); + + LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK ); + return( rc ); +} +#else /* LDAP_SASLIO_HOOKS */ +/* stubs for platforms that do not support SASL */ +int +LDAP_CALL +ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn, + const char *saslMechanism, + LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, + LDAP_SASL_INTERACT_PROC *callback, void *defaults ) +{ + return LDAP_SUCCESS; +} + +int +LDAP_CALL +ldap_sasl_interactive_bind_ext_s( LDAP *ld, const char *dn, + const char *saslMechanism, + LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags, + LDAP_SASL_INTERACT_PROC *callback, void *defaults, LDAPControl ***rctrl ) +{ + return LDAP_SUCCESS; +} +#endif /* LDAP_SASLIO_HOOKS */ + + +/* returns an LDAP error code that indicates if parse succeeded or not */ +int +LDAP_CALL +ldap_parse_sasl_bind_result( + LDAP *ld, + LDAPMessage *res, + struct berval **servercredp, + int freeit +) +{ + BerElement ber; + int rc, err; + ber_int_t along; + ber_len_t len; + char *m, *e; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 ); + + /* + * the ldapv3 SASL bind response looks like this: + * + * BindResponse ::= [APPLICATION 1] SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds [7] OCTET STRING OPTIONAL + * } + * + * all wrapped up in an LDAPMessage sequence. + */ + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || + !NSLDAPI_VALID_LDAPMESSAGE_BINDRESULT_POINTER( res )) { + return( LDAP_PARAM_ERROR ); + } + + /* only ldapv3 or higher can do sasl binds */ + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + if ( servercredp != NULL ) { + *servercredp = NULL; + } + + ber = *(res->lm_ber); /* struct copy */ + + /* skip past message id, matched dn, error message ... */ + rc = ber_scanf( &ber, "{iaa}", &along, &m, &e ); + + if ( rc != LBER_ERROR && + ber_peek_tag( &ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) { + rc = ber_get_stringal( &ber, servercredp ); + } + + if ( freeit ) { + ldap_msgfree( res ); + } + + if ( rc == LBER_ERROR ) { + err = LDAP_DECODING_ERROR; + } else { + err = (int) along; + } + + LDAP_SET_LDERRNO( ld, err, m, e ); + /* this is a little kludge for the 3.0 Barracuda/hammerhead relese */ + /* the docs state that the return is either LDAP_DECODING_ERROR */ + /* or LDAP_SUCCESS. Here we match the docs... it's cleaner in 3.1 */ + + if ( LDAP_DECODING_ERROR == err ) { + return (LDAP_DECODING_ERROR); + } else { + return( LDAP_SUCCESS ); + } +} + diff --git a/ldap/c-sdk/libraries/libldap/saslio.c b/ldap/c-sdk/libraries/libldap/saslio.c new file mode 100644 index 0000000000..6ab6303d0c --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/saslio.c @@ -0,0 +1,635 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef LDAP_SASLIO_HOOKS +#include +#include "ldap-int.h" +#include "../liblber/lber-int.h" +#include +/* Should be pulled in from lber-int.h */ +#define READBUFSIZ 8192 + +#define SEARCH_TIMEOUT_SECS 120 +#define NSLDAPI_SM_BUF 128 + +/* + * Data structures: + */ + +/* data structure that populates the I/O callback socket-specific arg. */ +typedef struct lextiof_socket_private { + struct ldap_x_ext_io_fns sess_io_fns; /* the saved layered ld fns from the layer we are "pushing" */ + struct lber_x_ext_io_fns sock_io_fns; /* the saved layered ber fns from the layer we are "pushing" */ + sasl_conn_t *sasl_ctx; /* the sasl context - pointer to the one from the connection */ + char *sb_sasl_ibuf; /* sasl decrypted input buffer */ + char *sb_sasl_iptr; /* current location in buffer */ + int sb_sasl_bfsz; /* Alloc'd size of input buffer */ + int sb_sasl_ilen; /* remaining length to process */ + LDAP *ld; /* used to set errno */ + Sockbuf *sb; /* pointer to our associated sockbuf */ +} SASLIOSocketArg; + +static void +destroy_SASLIOSocketArg(SASLIOSocketArg** sockarg) +{ + if (sockarg && *sockarg) { + NSLDAPI_FREE((*sockarg)->sb_sasl_ibuf); + NSLDAPI_FREE((*sockarg)); + *sockarg = NULL; + } +} + +static SASLIOSocketArg* +new_SASLIOSocketArg(sasl_conn_t *ctx, int bufsiz, LDAP *ld, Sockbuf *sb) +{ + SASLIOSocketArg *sockarg = NULL; + + if (bufsiz <= 0) { + return sockarg; + } + + sockarg = (SASLIOSocketArg*)NSLDAPI_CALLOC(1, sizeof(SASLIOSocketArg)); + if (sockarg) { + sockarg->sasl_ctx = ctx; + sockarg->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz); + if (!sockarg->sb_sasl_ibuf) { + destroy_SASLIOSocketArg(&sockarg); + return sockarg; + } + sockarg->sb_sasl_iptr = NULL; + sockarg->sb_sasl_bfsz = bufsiz; + sockarg->sb_sasl_ilen = 0; + sockarg->ld = ld; + sockarg->sb = sb; + } + + return sockarg; +} + +/* + * SASL Dependent routines + * + * SASL security and integrity options are supported through the + * use of the extended I/O functionality. Because the extended + * I/O functions may already be in use prior to enabling encryption, + * when SASL encryption si enabled, these routine interpose themselves + * over the exitng extended I/O routines and add an additional level + * of indirection. + * IE: Before SASL: client->libldap->lber->extio + * After SASL: client->libldap->lber->saslio->extio + * Any extio function are stilled used for the raw i/O [IE prldap] + * but SASL will decrypt before passing to lber. + * SASL cannot decrypt a stream so full packaets must be read + * before proceeding. + */ + +/* + * Get the 4 octet header [size] for a sasl encrypted buffer. + * See RFC222 [section 3]. + */ +static int +nsldapi_sasl_pktlen( char *buf, int maxbufsize ) +{ + int size; + +#if defined( _WINDOWS ) || defined( _WIN32 ) + size = ntohl(*(u_long *)buf); +#else + size = ntohl(*(uint32_t *)buf); +#endif + if ( size < 0 || size > maxbufsize ) { + return (-1 ); + } + + return( size + 4 ); /* include the first 4 bytes */ +} + +/* + * SASL encryption routines + */ + +static int +nsldapi_sasl_read( int s, void *buf, int len, + struct lextiof_socket_private *arg) +{ + LDAP *ld; + const char *dbuf; + char *cp; + int ret; + unsigned dlen, blen; + + ld = (LDAP *)arg->ld; + + /* Is there anything left in the existing buffer? */ + if ((ret = arg->sb_sasl_ilen) > 0) { + ret = (ret > len ? len : ret); + SAFEMEMCPY( buf, arg->sb_sasl_iptr, ret ); + if (ret == arg->sb_sasl_ilen) { + arg->sb_sasl_ilen = 0; + arg->sb_sasl_iptr = NULL; + } else { + arg->sb_sasl_ilen -= ret; + arg->sb_sasl_iptr += ret; + } + return( ret ); + } + + /* buffer is empty - fill it */ + cp = arg->sb_sasl_ibuf; + dlen = 0; + + /* Read the length of the packet */ + while ( dlen < 4 ) { + if (arg->sock_io_fns.lbextiofn_read != NULL) { + ret = arg->sock_io_fns.lbextiofn_read( + s, cp, 4 - dlen, + arg->sock_io_fns.lbextiofn_socket_arg); + } else { + ret = read( s, cp, 4 - dlen ); + } +#ifdef EINTR + if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return( ret ); + + cp += ret; + dlen += ret; + } + + blen = 4; + + ret = nsldapi_sasl_pktlen( arg->sb_sasl_ibuf, arg->sb_sasl_bfsz ); + if (ret < 0) { + LDAP_SET_ERRNO(ld, EIO); + return( -1 ); + } + dlen = ret - dlen; + + /* read the rest of the encrypted packet */ + while ( dlen > 0 ) { + if (arg->sock_io_fns.lbextiofn_read != NULL) { + ret = arg->sock_io_fns.lbextiofn_read( + s, cp, dlen, + arg->sock_io_fns.lbextiofn_socket_arg); + } else { + ret = read( s, cp, dlen ); + } + +#ifdef EINTR + if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return( ret ); + + cp += ret; + blen += ret; + dlen -= ret; + } + + /* Decode the packet */ + ret = sasl_decode( arg->sasl_ctx, + arg->sb_sasl_ibuf, blen, + &dbuf, &dlen); + if ( ret != SASL_OK ) { + /* sb_sasl_read: failed to decode packet, drop it, error */ + arg->sb_sasl_iptr = NULL; + arg->sb_sasl_ilen = 0; + LDAP_SET_ERRNO(ld, EIO); + return( -1 ); + } + + /* copy decrypted packet to the input buffer */ + SAFEMEMCPY( arg->sb_sasl_ibuf, dbuf, dlen ); + arg->sb_sasl_iptr = arg->sb_sasl_ibuf; + arg->sb_sasl_ilen = dlen; + + ret = (dlen > (unsigned) len ? len : dlen); + SAFEMEMCPY( buf, arg->sb_sasl_iptr, ret ); + if (ret == arg->sb_sasl_ilen) { + arg->sb_sasl_ilen = 0; + arg->sb_sasl_iptr = NULL; + } else { + arg->sb_sasl_ilen -= ret; + arg->sb_sasl_iptr += ret; + } + return( ret ); +} + +static int +nsldapi_sasl_write( int s, const void *buf, int len, + struct lextiof_socket_private *arg) +{ + int ret = 0; + const char *obuf, *optr, *cbuf = (const char *)buf; + unsigned olen, clen, tlen = 0; + unsigned *maxbuf; + + ret = sasl_getprop(arg->sasl_ctx, SASL_MAXOUTBUF, + (const void **)&maxbuf); + if ( ret != SASL_OK ) { + /* just a sanity check, should never happen */ + return( -1 ); + } + + while (len > 0) { + clen = (len > *maxbuf) ? *maxbuf : len; + /* encode the next packet. */ + ret = sasl_encode( arg->sasl_ctx, cbuf, clen, &obuf, &olen); + if ( ret != SASL_OK ) { + /* XXX Log error? "sb_sasl_write: failed to encode packet..." */ + return( -1 ); + } + /* Write everything now, buffer is only good until next sasl_encode */ + optr = obuf; + while (olen > 0) { + if (arg->sock_io_fns.lbextiofn_write != NULL) { + ret = arg->sock_io_fns.lbextiofn_write( + s, optr, olen, + arg->sock_io_fns.lbextiofn_socket_arg); + } else { + ret = write( s, optr, olen); + } + if ( ret < 0 ) + return( ret ); + optr += ret; + olen -= ret; + } + len -= clen; + cbuf += clen; + tlen += clen; + } + return( tlen ); +} + +/* + * What's all this then? + * First, take a look at os-ip.c:nsldapi_add_to_cb_pollfds(). When a new descriptor is + * added to the pollfds array, the lpoll_socketarg field is initialized to the value from + * the socketarg field - sb->sb_ext_io_fns.lbextiofn_socket_arg. In our case, since we + * override this with our sasl data (see below nsldapi_sasl_install), we need to restore + * the previous value so that the layer below us (i.e. prldap) can use the lpoll_socketarg + * which it sets. + * So how do which know which fds[i] is a "sasl" fd? + * We initialize the lextiof_session_private *arg (see nsldapi_sasl_install) to point to + * the socket_private data in sb->sb_ext_io_fns.lbextiofn_socket_arg for "sasl" sockets, + * which is then used to initialize lpoll_socketarg (see above). + * So, if the arg which gets passed into nsldapi_sasl_poll is the same as the + * fds[i].lpoll_socketarg, we know it is a "sasl" socket and we need to "pop" the sasl + * layer. We do this by replacing lpoll_socketarg with the one we saved when we "pushed" + * the sasl layer. + * So why the loop to restore the sasl lpoll_socketarg? + * The lower layer only uses lpoll_socketarg during poll(). See ldappr-io.c:prldap_poll() + * for more information about how that works. However, after the polling is done, there + * is some special magic in os-ip.c in the functions nsldapi_add_to_cb_pollfds(), + * nsldapi_clear_from_cb_pollfds(), and nsldapi_find_in_cb_pollfds() to find the correct + * Sockbuf to operate on. This is the macro NSLDAPI_CB_POLL_MATCH(). For the extended + * io function callbacks to work correctly, it is not sufficient to say that the file + * descriptor in the Sockbuf matches the one that poll says has activity - we also need + * to match the lpoll_socketarg with the sb->sb_ext_io_fns.lbextiofn_socket_arg to make + * sure this really is the Sockbuf we want to use. So we have to restore the + * lpoll_socketarg with the original one. + * Why have origarg and staticorigarg? + * To avoid malloc. The sizeof staticorigarg should be large enough to accomodate almost + * all clients without incurring too much additional overhead. However, if we need more + * room, origarg will grow to nfds. If this proves to be inadequate, the size of the + * staticorigarg is a good candidate for a #define set by configure. + */ +static int +nsldapi_sasl_poll( + LDAP_X_PollFD fds[], int nfds, int timeout, + struct lextiof_session_private *arg ) +{ + LDAP_X_EXTIOF_POLL_CALLBACK *origpoll; /* poll fn from the pushed layer */ + struct lextiof_session_private *origsess = NULL; /* session arg from the pushed layer */ + SASLIOSocketArg **origarg = NULL; /* list of saved original socket args */ + SASLIOSocketArg *staticorigarg[1024]; /* default list to avoid malloc */ + int origargsize = sizeof(staticorigarg)/sizeof(staticorigarg[0]); + int rc = -1; /* the return code - -1 means failure */ + + if (arg == NULL) { /* should not happen */ + return( rc ); + } + + origarg = staticorigarg; + /* if the static array is not large enough, alloc a dynamic one */ + if (origargsize < nfds) { + origarg = (SASLIOSocketArg **)NSLDAPI_MALLOC(nfds*sizeof(SASLIOSocketArg *)); + } + + if (fds && nfds > 0) { + int i; + for(i = 0; i < nfds; i++) { + /* save the original socket arg */ + origarg[i] = fds[i].lpoll_socketarg; + if (arg == (struct lextiof_session_private *)fds[i].lpoll_socketarg) { + /* lpoll_socketarg is a sasl socket arg - we need to replace it + with the one from the layer we pushed (i.e. prldap) */ + SASLIOSocketArg *sockarg = (SASLIOSocketArg *)fds[i].lpoll_socketarg; + /* reset to pushed layer's socket arg */ + fds[i].lpoll_socketarg = sockarg->sock_io_fns.lbextiofn_socket_arg; + /* grab the pushed layers' poll fn and its session arg */ + if (!origsess) { + origpoll = sockarg->sess_io_fns.lextiof_poll; + origsess = sockarg->sess_io_fns.lextiof_session_arg; + } + } + } + } + + if (origsess == NULL) { /* should not happen */ + goto done; + } + + /* call the "real" poll function */ + rc = origpoll( fds, nfds, timeout, origsess ); + + /* reset the lpoll_socketarg values to their original values because + they must match what's in sb->iofns->lbextiofn_socket_arg in order + for NSLDAPI_CB_POLL_MATCH to work - see os-ip.c */ + if (fds && nfds > 0) { + int i; + for(i = 0; i < nfds; i++) { + if ((SASLIOSocketArg *)arg == origarg[i]) { + fds[i].lpoll_socketarg = origarg[i]; + } + } + } + +done: + /* if we had to use a dynamic array, free it */ + if (origarg != staticorigarg) { + NSLDAPI_FREE(origarg); + } + + return rc; +} + +int +nsldapi_sasl_open( LDAP *ld, LDAPConn *lconn, sasl_conn_t **ctx, sasl_ssf_t ssf ) +{ + int saslrc; + char *host = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + if ( lconn == NULL ) { + if ( ld->ld_defconn == NULL || + ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) { + int rc = nsldapi_open_ldap_defconn( ld ); + if( rc < 0 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + } + lconn = ld->ld_defconn; + } + + /* need to clear out the old context for this connection, if any */ + /* client may have re-bind-ed this connection without closing first */ + if (lconn->lconn_sasl_ctx) { + sasl_dispose(&lconn->lconn_sasl_ctx); + lconn->lconn_sasl_ctx = NULL; + } + + if ( 0 != ldap_get_option( ld, LDAP_OPT_HOST_NAME, &host ) ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + saslrc = sasl_client_new( "ldap", host, + NULL, NULL, /* iplocalport, ipremoteport - use defaults */ + NULL, 0, ctx ); + ldap_memfree(host); + + if ( (saslrc != SASL_OK) || (!*ctx) ) { + return( nsldapi_sasl_cvterrno( ld, saslrc, NULL ) ); + } + + if( ssf ) { + sasl_ssf_t extprops; + memset(&extprops, 0L, sizeof(extprops)); + extprops = ssf; + + (void) sasl_setprop( *ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); + } + + /* (re)set security properties */ + sasl_setprop( *ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops ); + + /* set the connection context */ + lconn->lconn_sasl_ctx = *ctx; + + return( LDAP_SUCCESS ); +} + +static int +nsldapi_sasl_close( struct lextiof_socket_private *arg ) +{ + /* undo function pointer interposing */ + ldap_set_option( arg->ld, LDAP_X_OPT_EXTIO_FN_PTRS, &arg->sess_io_fns ); + /* have to do this separately to make sure the socketarg is set correctly */ + ber_sockbuf_set_option( arg->sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&arg->sock_io_fns ); + + destroy_SASLIOSocketArg(&arg); + return( LDAP_SUCCESS ); +} + +static int +nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg ) +{ + LDAP_X_EXTIOF_CLOSE_CALLBACK *origclose; + struct lextiof_socket_private *origsock; + + if (arg == NULL) { + return( -1 ); + } + + origclose = arg->sess_io_fns.lextiof_close; + origsock = arg->sock_io_fns.lbextiofn_socket_arg; + + /* undo SASL */ + nsldapi_sasl_close( arg ); + arg = NULL; + /* arg is destroyed at this point - do not use it */ + + if (origclose ) + return ( origclose( s, origsock ) ); + else { + /* This is a copy of nsldapi_os_closesocket() + * from os-ip.c. It is declared static there, + * hence the copy of it. + */ + int rc; + +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef _WINDOWS + rc = closesocket( s ); +#else /* _WINDOWS */ + rc = close( s ); +#endif /* _WINDOWS */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + return( rc ); + } + +} + +/* + * install encryption routines if security has been negotiated + */ +int +nsldapi_sasl_install( LDAP *ld, LDAPConn *lconn ) +{ + struct lber_x_ext_io_fns fns; + struct ldap_x_ext_io_fns iofns; + sasl_security_properties_t *secprops; + int rc, value; + int bufsiz; + Sockbuf *sb = NULL; + sasl_conn_t *ctx = NULL; + SASLIOSocketArg *sockarg = NULL; + + if ( lconn == NULL ) { + lconn = ld->ld_defconn; + if ( lconn == NULL ) { + return( LDAP_LOCAL_ERROR ); + } + } + if ( (sb = lconn->lconn_sb) == NULL ) { + return( LDAP_LOCAL_ERROR ); + } + rc = ber_sockbuf_get_option( sb, + LBER_SOCKBUF_OPT_TO_FILE_ONLY, + (void *) &value); + if (rc != 0 || value != 0) { + return( LDAP_LOCAL_ERROR ); + } + + /* the sasl context in the lconn must have been set prior to this */ + ctx = lconn->lconn_sasl_ctx; + rc = sasl_getprop( ctx, SASL_SEC_PROPS, + (const void **)&secprops ); + if (rc != SASL_OK) + return( LDAP_LOCAL_ERROR ); + bufsiz = secprops->maxbufsize; + if (bufsiz <= 0) { + return( LDAP_LOCAL_ERROR ); + } + + /* create our socket specific context */ + sockarg = new_SASLIOSocketArg(ctx, bufsiz, ld, sb); + if (!sockarg) { + return( LDAP_LOCAL_ERROR ); + } + + /* save a copy of the existing io fns and the session arg */ + memset( &sockarg->sess_io_fns, 0, LDAP_X_EXTIO_FNS_SIZE ); + sockarg->sess_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, + &sockarg->sess_io_fns ); + if (rc != 0) { + destroy_SASLIOSocketArg(&sockarg); + return( LDAP_LOCAL_ERROR ); + } + + /* save a copy of the existing ber io fns and the socket arg */ + memset( &sockarg->sock_io_fns, 0, LBER_X_EXTIO_FNS_SIZE ); + sockarg->sock_io_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + rc = ber_sockbuf_get_option( sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&sockarg->sock_io_fns); + if (rc != 0) { + destroy_SASLIOSocketArg(&sockarg); + return( LDAP_LOCAL_ERROR ); + } + + /* Always set the ext io close fn pointer to ensure we + * clean up our sockarg context */ + memset( &iofns, 0, sizeof(iofns)); + /* first, copy struct - sets defaults */ + iofns = sockarg->sess_io_fns; + iofns.lextiof_close = nsldapi_sasl_close_socket; + iofns.lextiof_session_arg = sockarg; /* needed for close and poll */ + + /* Set new values for the other ext io funcs if there are any - + when using the native io fns (as opposed to prldap) there + won't be any */ + if ( sockarg->sess_io_fns.lextiof_read != NULL || + sockarg->sess_io_fns.lextiof_write != NULL || + sockarg->sess_io_fns.lextiof_poll != NULL || + sockarg->sess_io_fns.lextiof_connect != NULL ) { + /* next, just reset those functions we want to override */ + iofns.lextiof_read = nsldapi_sasl_read; + iofns.lextiof_write = nsldapi_sasl_write; + iofns.lextiof_poll = nsldapi_sasl_poll; + } + + /* set the ext io funcs */ + rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &iofns ); + if (rc != 0) { + /* frees everything and resets fns above */ + nsldapi_sasl_close(sockarg); + return( LDAP_LOCAL_ERROR ); + } + + /* set the new ber io funcs and socket arg */ + (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE); + fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + fns.lbextiofn_read = nsldapi_sasl_read; + fns.lbextiofn_write = nsldapi_sasl_write; + fns.lbextiofn_socket_arg = sockarg; + rc = ber_sockbuf_set_option( sb, + LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&fns); + if (rc != 0) { + /* frees everything and resets fns above */ + nsldapi_sasl_close(sockarg); + return( LDAP_LOCAL_ERROR ); + } + + return( LDAP_SUCCESS ); +} + +#endif diff --git a/ldap/c-sdk/libraries/libldap/sbind.c b/ldap/c-sdk/libraries/libldap/sbind.c new file mode 100644 index 0000000000..d9bfc71fd4 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/sbind.c @@ -0,0 +1,214 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * sbind.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +static int simple_bind_nolock( LDAP *ld, const char *dn, const char *passwd, + int unlock_permitted ); + +/* + * ldap_simple_bind - bind to the ldap server. The dn and + * password of the entry to which to bind are supplied. The message id + * of the request initiated is returned. + * + * Example: + * ldap_simple_bind( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ + +int +LDAP_CALL +ldap_simple_bind( LDAP *ld, const char *dn, const char *passwd ) +{ + int rc; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_simple_bind\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ( ld->ld_options & LDAP_BITOPT_RECONNECT ) != 0 ) { + nsldapi_handle_reconnect( ld ); + } + + rc = simple_bind_nolock( ld, dn, passwd, 1 ); + + return( rc ); +} + + +static int +simple_bind_nolock( LDAP *ld, const char *dn, const char *passwd, + int unlock_permitted ) +{ + BerElement *ber; + int rc, msgid; + + /* + * The bind request looks like this: + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * } + * } + * all wrapped up in an LDAPMessage sequence. + */ + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + if ( dn == NULL ) + dn = ""; + if ( passwd == NULL ) + passwd = ""; + + if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) { + struct berval bv; + + bv.bv_val = (char *)passwd; + bv.bv_len = strlen( passwd ); + /* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */ + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn, &bv, + LDAP_AUTH_SIMPLE ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + /* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */ + if ( rc != 0 ) { + return( rc ); + } + } + + /* create a message to send */ + if (( rc = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( -1 ); + } + + /* fill it in */ + if ( ber_printf( ber, "{it{ists}", msgid, LDAP_REQ_BIND, + NSLDAPI_LDAP_VERSION( ld ), dn, LDAP_AUTH_SIMPLE, passwd ) == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( -1 ); + } + + if ( nsldapi_put_controls( ld, NULL, 1, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( -1 ); + } + + /* send the message */ + return( nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND, + (char *)dn, ber )); +} + + +/* + * ldap_simple_bind - bind to the ldap server using simple + * authentication. The dn and password of the entry to which to bind are + * supplied. LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + * + * Example: + * ldap_simple_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ +int +LDAP_CALL +ldap_simple_bind_s( LDAP *ld, const char *dn, const char *passwd ) +{ + int msgid; + LDAPMessage *result; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_simple_bind_s\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( (msgid = ldap_simple_bind( ld, dn, passwd )) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) == -1 ) + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + + return( ldap_result2error( ld, result, 1 ) ); +} + +void nsldapi_handle_reconnect( LDAP *ld ) +{ + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_handle_reconnect\n", 0, 0, 0 ); + + /* + * if the default connection has been lost and is now marked dead, + * dispose of the default connection so it will get re-established. + * + * if not, clear the bind DN and status to ensure that we don't + * report the wrong bind DN to a different thread while waiting + * for our bind result to return from the server. + */ + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + if ( NULL != ld->ld_defconn ) { + if ( LDAP_CONNST_DEAD == ld->ld_defconn->lconn_status ) { + nsldapi_free_connection( ld, ld->ld_defconn, NULL, NULL, 1, 0 ); + ld->ld_defconn = NULL; + } else if ( ld->ld_defconn->lconn_binddn != NULL ) { + NSLDAPI_FREE( ld->ld_defconn->lconn_binddn ); + ld->ld_defconn->lconn_binddn = NULL; + ld->ld_defconn->lconn_bound = 0; + } + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); +} diff --git a/ldap/c-sdk/libraries/libldap/search.c b/ldap/c-sdk/libraries/libldap/search.c new file mode 100644 index 0000000000..648d739e6a --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/search.c @@ -0,0 +1,1022 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * search.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +static int nsldapi_timeval2ldaplimit( struct timeval *timeoutp, + int defaultvalue ); +static int nsldapi_search( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + int timelimit, int sizelimit, int *msgidp ); +static char *find_right_paren( char *s ); +static char *put_complex_filter( BerElement *ber, char *str, + unsigned long tag, int not ); +static int put_filter( BerElement *ber, char *str ); +static int unescape_filterval( char *str ); +static int hexchar2int( char c ); +static int is_valid_attr( char *a ); +static int put_simple_filter( BerElement *ber, char *str ); +static int put_substring_filter( BerElement *ber, char *type, + char *str ); +static int put_filter_list( BerElement *ber, char *str ); +static int nsldapi_search_s( LDAP *ld, const char *base, int scope, + const char *filter, char **attrs, int attrsonly, + LDAPControl **serverctrls, LDAPControl **clientctrls, + struct timeval *localtimeoutp, int timelimit, int sizelimit, + LDAPMessage **res ); + +/* + * ldap_search - initiate an ldap search operation. Parameters: + * + * ld LDAP descriptor + * base DN of the base object + * scope the search scope - one of LDAP_SCOPE_BASE, + * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE + * filter a string containing the search filter + * (e.g., "(|(cn=bob)(sn=bob))") + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob", + * attrs, attrsonly ); + */ +int +LDAP_CALL +ldap_search( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly +) +{ + int msgid; + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 ); + + if ( ldap_search_ext( ld, base, scope, filter, attrs, attrsonly, NULL, + NULL, NULL, -1, &msgid ) == LDAP_SUCCESS ) { + return( msgid ); + } else { + return( -1 ); /* error is in ld handle */ + } +} + + +/* + * LDAPv3 extended search. + * Returns an LDAP error code. + */ +int +LDAP_CALL +ldap_search_ext( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct timeval *timeoutp, /* NULL means use ld->ld_timelimit */ + int sizelimit, + int *msgidp +) +{ + /* + * It is an error to pass in a zero'd timeval. + */ + if ( timeoutp != NULL && timeoutp->tv_sec == 0 && + timeoutp->tv_usec == 0 ) { + if ( ld != NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + } + return( LDAP_PARAM_ERROR ); + } + + return( nsldapi_search( ld, base, scope, filter, attrs, attrsonly, + serverctrls, clientctrls, + nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, msgidp )); +} + + +/* + * Like ldap_search_ext() except an integer timelimit is passed instead of + * using the overloaded struct timeval *timeoutp. + */ +static int +nsldapi_search( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int timelimit, /* -1 means use ld->ld_timelimit */ + int sizelimit, /* -1 means use ld->ld_sizelimit */ + int *msgidp +) +{ + BerElement *ber; + int rc, rc_key; + unsigned long key; /* XXXmcs: memcache */ + + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 ); + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( base == NULL ) { + base = ""; + } + + if ( filter == NULL ) { + filter = "(objectclass=*)"; + } + + if ( msgidp == NULL || ( scope != LDAP_SCOPE_BASE + && scope != LDAP_SCOPE_ONELEVEL && scope != LDAP_SCOPE_SUBTREE ) + || ( sizelimit < -1 )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + *msgidp = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + /* + * XXXmcs: should use cache function pointers to hook in memcache + */ + if ( ld->ld_memcache == NULL ) { + rc_key = LDAP_NOT_SUPPORTED; + } else if (( rc_key = ldap_memcache_createkey( ld, base, scope, filter, + attrs, attrsonly, serverctrls, clientctrls, &key)) == LDAP_SUCCESS + && ldap_memcache_result( ld, *msgidp, key ) == LDAP_SUCCESS ) { + return LDAP_SUCCESS; + } + + /* check the cache */ + if ( ld->ld_cache_on && ld->ld_cache_search != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + if ( (rc = (ld->ld_cache_search)( ld, *msgidp, LDAP_REQ_SEARCH, + base, scope, filter, attrs, attrsonly )) != 0 ) { + *msgidp = rc; + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + return( LDAP_SUCCESS ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* caching off or did not find it in the cache - check the net */ + if (( rc = nsldapi_build_search_req( ld, base, scope, filter, attrs, + attrsonly, serverctrls, clientctrls, timelimit, sizelimit, + *msgidp, &ber )) != LDAP_SUCCESS ) { + return( rc ); + } + + /* send the message */ + rc = nsldapi_send_initial_request( ld, *msgidp, LDAP_REQ_SEARCH, + (char *) base, ber ); + + /* + * XXXmcs: should use cache function pointers to hook in memcache + */ + if ( (rc_key == LDAP_SUCCESS) && (rc >= 0) ) { + ldap_memcache_new( ld, rc, key, base ); + } + + *msgidp = rc; + return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS ); +} + + +/* + * Convert a non-NULL timeoutp to a value in seconds that is appropriate to + * send in an LDAP search request. If timeoutp is NULL, return defaultvalue. + */ +static int +nsldapi_timeval2ldaplimit( struct timeval *timeoutp, int defaultvalue ) +{ + int timelimit; + + if ( NULL == timeoutp ) { + timelimit = defaultvalue; + } else if ( timeoutp->tv_sec > 0 ) { + timelimit = timeoutp->tv_sec; + } else if ( timeoutp->tv_usec > 0 ) { + timelimit = 1; /* minimum we can express in LDAP */ + } else { + /* + * both tv_sec and tv_usec are less than one (zero?) so + * to maintain compatiblity with our "zero means no limit" + * convention we pass no limit to the server. + */ + timelimit = 0; /* no limit */ + } + + return( timelimit ); +} + + +/* returns an LDAP error code and also sets it in ld */ +int +nsldapi_build_search_req( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, /* not used for anything yet */ + int timelimit, /* if -1, ld->ld_timelimit is used */ + int sizelimit, /* if -1, ld->ld_sizelimit is used */ + int msgid, + BerElement **berp +) +{ + BerElement *ber; + int err; + char *fdup; + + /* + * Create the search request. It looks like this: + * SearchRequest := [APPLICATION 3] SEQUENCE { + * baseObject DistinguishedName, + * scope ENUMERATED { + * baseObject (0), + * singleLevel (1), + * wholeSubtree (2) + * }, + * derefAliases ENUMERATED { + * neverDerefaliases (0), + * derefInSearching (1), + * derefFindingBaseObj (2), + * alwaysDerefAliases (3) + * }, + * sizelimit INTEGER (0 .. 65535), + * timelimit INTEGER (0 .. 65535), + * attrsOnly BOOLEAN, + * filter Filter, + * attributes SEQUENCE OF AttributeType + * } + * wrapped in an ldap message. + */ + + /* create a message to send */ + if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( err ); + } + + if ( base == NULL ) { + base = ""; + } + + if ( sizelimit == -1 ) { + sizelimit = ld->ld_sizelimit; + } + + if ( timelimit == -1 ) { + timelimit = ld->ld_timelimit; + } + +#ifdef CLDAP + if ( ld->ld_sbp->sb_naddr > 0 ) { + err = ber_printf( ber, "{ist{seeiib", msgid, + ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref, + sizelimit, timelimit, attrsonly ); + } else { +#endif /* CLDAP */ + err = ber_printf( ber, "{it{seeiib", msgid, + LDAP_REQ_SEARCH, base, scope, ld->ld_deref, + sizelimit, timelimit, attrsonly ); +#ifdef CLDAP + } +#endif /* CLDAP */ + + if ( err == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + fdup = nsldapi_strdup( filter ); + err = put_filter( ber, fdup ); + NSLDAPI_FREE( fdup ); + + if ( err == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_FILTER_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_FILTER_ERROR ); + } + + if ( ber_printf( ber, "{v}}", attrs ) == -1 ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if ( (err = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( err ); + } + + *berp = ber; + return( LDAP_SUCCESS ); +} + +static char * +find_right_paren( char *s ) +{ + int balance, escape; + + balance = 1; + escape = 0; + while ( *s && balance ) { + if ( escape == 0 ) { + if ( *s == '(' ) + balance++; + else if ( *s == ')' ) + balance--; + } + if ( *s == '\\' && ! escape ) + escape = 1; + else + escape = 0; + if ( balance ) + s++; + } + + return( *s ? s : NULL ); +} + +static char * +put_complex_filter( + BerElement *ber, + char *str, + unsigned long tag, + int not +) +{ + char *next; + + /* + * We have (x(filter)...) with str sitting on + * the x. We have to find the paren matching + * the one before the x and put the intervening + * filters by calling put_filter_list(). + */ + + /* put explicit tag */ + if ( ber_printf( ber, "t{", tag ) == -1 ) + return( NULL ); + + str++; + if ( (next = find_right_paren( str )) == NULL ) + return( NULL ); + + *next = '\0'; + if ( put_filter_list( ber, str ) == -1 ) + return( NULL ); + *next++ = ')'; + + /* flush explicit tagged thang */ + if ( ber_printf( ber, "}" ) == -1 ) + return( NULL ); + + return( next ); +} + +static int +put_filter( BerElement *ber, char *str ) +{ + char *next; + int parens, balance, escape; + + /* + * A Filter looks like this: + * Filter ::= CHOICE { + * and [0] SET OF Filter, + * or [1] SET OF Filter, + * not [2] Filter, + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeType,, + * approxMatch [8] AttributeValueAssertion + * } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeType, + * SEQUENCE OF CHOICE { + * initial [0] IA5String, + * any [1] IA5String, + * final [2] IA5String + * } + * } + * Note: tags in a choice are always explicit + */ + + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter \"%s\"\n", str, 0, 0 ); + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': + str++; + parens++; + switch ( *str ) { + case '&': + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: AND\n", + 0, 0, 0 ); + + if ( (str = put_complex_filter( ber, str, + LDAP_FILTER_AND, 0 )) == NULL ) + return( -1 ); + + parens--; + break; + + case '|': + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: OR\n", + 0, 0, 0 ); + + if ( (str = put_complex_filter( ber, str, + LDAP_FILTER_OR, 0 )) == NULL ) + return( -1 ); + + parens--; + break; + + case '!': + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: NOT\n", + 0, 0, 0 ); + + if ( (str = put_complex_filter( ber, str, + LDAP_FILTER_NOT, 1 )) == NULL ) + return( -1 ); + + parens--; + break; + + default: + LDAPDebug( LDAP_DEBUG_TRACE, + "put_filter: simple\n", 0, 0, 0 ); + + balance = 1; + escape = 0; + next = str; + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) + balance++; + else if ( *next == ')' ) + balance--; + } + if ( *next == '\\' && ! escape ) + escape = 1; + else + escape = 0; + if ( balance ) + next++; + } + if ( balance != 0 ) + return( -1 ); + + *next = '\0'; + if ( put_simple_filter( ber, str ) == -1 ) { + return( -1 ); + } + *next++ = ')'; + str = next; + parens--; + break; + } + break; + + case ')': + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: end\n", 0, 0, + 0 ); + if ( ber_printf( ber, "]" ) == -1 ) + return( -1 ); + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter: default\n", 0, 0, + 0 ); + next = strchr( str, '\0' ); + if ( put_simple_filter( ber, str ) == -1 ) { + return( -1 ); + } + str = next; + break; + } + } + + return( parens ? -1 : 0 ); +} + + +/* + * Put a list of filters like this "(filter1)(filter2)..." + */ + +static int +put_filter_list( BerElement *ber, char *str ) +{ + char *next; + char save; + + LDAPDebug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", str, 0, 0 ); + + while ( *str ) { + while ( *str && isspace( *str ) ) + str++; + if ( *str == '\0' ) + break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) + return( -1 ); + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( put_filter( ber, str ) == -1 ) + return( -1 ); + *next = save; + + str = next; + } + + return( 0 ); +} + + +/* + * is_valid_attr - returns 1 if a is a syntactically valid left-hand side + * of a filter expression, 0 otherwise. A valid string may contain only + * letters, numbers, hyphens, semi-colons, colons and periods. examples: + * cn + * cn;lang-fr + * 1.2.3.4;binary;dynamic + * mail;dynamic + * cn:dn:1.2.3.4 + * + * For compatibility with older servers, we also allow underscores in + * attribute types, even through they are not allowed by the LDAPv3 RFCs. + */ +static int +is_valid_attr( char *a ) +{ + for ( ; *a; a++ ) { + if ( !isascii( *a ) ) { + return( 0 ); + } else if ( !isalnum( *a ) ) { + switch ( *a ) { + case '-': + case '.': + case ';': + case ':': + case '_': + break; /* valid */ + default: + return( 0 ); + } + } + } + + return( 1 ); +} + +static char * +find_star( char *s ) +{ + for ( ; *s; ++s ) { + switch ( *s ) { + case '*': return s; + case '\\': + ++s; + if ( hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0 ) ++s; + default: break; + } + } + return NULL; +} + +static int +put_simple_filter( BerElement *ber, char *str ) +{ + char *s, *s2, *s3, filterop; + char *value; + unsigned long ftype; + int rc, len; + char *oid; /* for v3 extended filter */ + int dnattr; /* for v3 extended filter */ + + LDAPDebug( LDAP_DEBUG_TRACE, "put_simple_filter \"%s\"\n", str, 0, 0 ); + + rc = -1; /* pessimistic */ + + if (( str = nsldapi_strdup( str )) == NULL ) { + return( rc ); + } + + if ( (s = strchr( str, '=' )) == NULL ) { + goto free_and_return; + } + value = s + 1; + *s-- = '\0'; + filterop = *s; + if ( filterop == '<' || filterop == '>' || filterop == '~' || + filterop == ':' ) { + *s = '\0'; + } + + if ( ! is_valid_attr( str ) ) { + goto free_and_return; + } + + switch ( filterop ) { + case '<': + ftype = LDAP_FILTER_LE; + break; + case '>': + ftype = LDAP_FILTER_GE; + break; + case '~': + ftype = LDAP_FILTER_APPROX; + break; + case ':': /* extended filter - v3 only */ + /* + * extended filter looks like this: + * + * [type][':dn'][':'oid]':='value + * + * where one of type or :oid is required. + * + */ + ftype = LDAP_FILTER_EXTENDED; + s2 = s3 = NULL; + if ( (s2 = strrchr( str, ':' )) == NULL ) { + goto free_and_return; + } + if ( strcasecmp( s2, ":dn" ) == 0 ) { + oid = NULL; + dnattr = 1; + *s2 = '\0'; + } else { + oid = s2 + 1; + dnattr = 0; + *s2 = '\0'; + if ( (s3 = strrchr( str, ':' )) != NULL ) { + if ( strcasecmp( s3, ":dn" ) == 0 ) { + dnattr = 1; + } else { + goto free_and_return; + } + *s3 = '\0'; + } + } + if ( (rc = ber_printf( ber, "t{", ftype )) == -1 ) { + goto free_and_return; + } + if ( oid != NULL ) { + if ( (rc = ber_printf( ber, "ts", LDAP_TAG_MRA_OID, + oid )) == -1 ) { + goto free_and_return; + } + } + if ( *str != '\0' ) { + if ( (rc = ber_printf( ber, "ts", + LDAP_TAG_MRA_TYPE, str )) == -1 ) { + goto free_and_return; + } + } + if (( len = unescape_filterval( value )) < 0 || + ( rc = ber_printf( ber, "totb}", LDAP_TAG_MRA_VALUE, + value, len, LDAP_TAG_MRA_DNATTRS, dnattr )) == -1 ) { + goto free_and_return; + } + rc = 0; + goto free_and_return; + break; + default: + if ( find_star( value ) == NULL ) { + ftype = LDAP_FILTER_EQUALITY; + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + } else { + rc = put_substring_filter( ber, str, value ); + goto free_and_return; + } + break; + } + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + } else if (( len = unescape_filterval( value )) >= 0 ) { + rc = ber_printf( ber, "t{so}", ftype, str, value, len ); + } + if ( rc != -1 ) { + rc = 0; + } + +free_and_return: + NSLDAPI_FREE( str ); + return( rc ); +} + + +/* + * Undo in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape + * sequences within the null-terminated string 'val'. The resulting value + * may contain null characters. + * + * If 'val' contains invalid escape sequences we return -1. + * Otherwise the length of the unescaped value is returned. + */ +static int +unescape_filterval( char *val ) +{ + int escape, firstdigit, ival; + char *s, *d; + + escape = firstdigit = 0; + for ( s = d = val; *s; s++ ) { + if ( escape ) { + /* + * first try LDAPv3 escape (hexadecimal) sequence + */ + if (( ival = hexchar2int( *s )) < 0 ) { + if ( firstdigit ) { + /* + * LDAPv2 (RFC1960) escape sequence + */ + *d++ = *s; + escape = 0; + } else { + return(-1); + } + } + if ( firstdigit ) { + *d = ( ival<<4 ); + firstdigit = 0; + } else { + *d++ |= ival; + escape = 0; + } + + } else if ( *s != '\\' ) { + *d++ = *s; + escape = 0; + + } else { + escape = 1; + firstdigit = 1; + } + } + + return( d - val ); +} + + +/* + * convert character 'c' that represents a hexadecimal digit to an integer. + * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned. + * otherwise the converted value is returned. + */ +static int +hexchar2int( char c ) +{ + if ( c >= '0' && c <= '9' ) { + return( c - '0' ); + } + if ( c >= 'A' && c <= 'F' ) { + return( c - 'A' + 10 ); + } + if ( c >= 'a' && c <= 'f' ) { + return( c - 'a' + 10 ); + } + return( -1 ); +} + +static int +put_substring_filter( BerElement *ber, char *type, char *val ) +{ + char *nextstar, gotstar = 0; + unsigned long ftype; + int len; + + LDAPDebug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", type, + val, 0 ); + + if ( ber_printf( ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type ) == -1 ) { + return( -1 ); + } + + for ( ; val != NULL; val = nextstar ) { + if ( (nextstar = find_star( val )) != NULL ) { + *nextstar++ = '\0'; + } + + if ( gotstar == 0 ) { + ftype = LDAP_SUBSTRING_INITIAL; + } else if ( nextstar == NULL ) { + ftype = LDAP_SUBSTRING_FINAL; + } else { + ftype = LDAP_SUBSTRING_ANY; + } + if ( *val != '\0' ) { + if (( len = unescape_filterval( val )) < 0 || + ber_printf( ber, "to", ftype, val, len ) == -1 ) { + return( -1 ); + } + } + + gotstar = 1; + } + + if ( ber_printf( ber, "}}" ) == -1 ) { + return( -1 ); + } + + return( 0 ); +} + +int +LDAP_CALL +ldap_search_st( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + struct timeval *timeout, + LDAPMessage **res +) +{ + return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, + NULL, NULL, timeout, -1, -1, res )); +} + +int +LDAP_CALL +ldap_search_s( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPMessage **res +) +{ + return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, + NULL, NULL, NULL, -1, -1, res )); +} + +int LDAP_CALL +ldap_search_ext_s( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct timeval *timeoutp, + int sizelimit, + LDAPMessage **res +) +{ + return( nsldapi_search_s( ld, base, scope, filter, attrs, attrsonly, + serverctrls, clientctrls, timeoutp, + nsldapi_timeval2ldaplimit( timeoutp, -1 ), sizelimit, res )); +} + + +static int +nsldapi_search_s( + LDAP *ld, + const char *base, + int scope, + const char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct timeval *localtimeoutp, + int timelimit, /* -1 means use ld->ld_timelimit */ + int sizelimit, /* -1 means use ld->ld_sizelimit */ + LDAPMessage **res +) +{ + int err, msgid; + + /* + * It is an error to pass in a zero'd timeval. + */ + if ( localtimeoutp != NULL && localtimeoutp->tv_sec == 0 && + localtimeoutp->tv_usec == 0 ) { + if ( ld != NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + } + if ( res != NULL ) { + *res = NULL; + } + return( LDAP_PARAM_ERROR ); + } + + if (( err = nsldapi_search( ld, base, scope, filter, attrs, attrsonly, + serverctrls, clientctrls, timelimit, sizelimit, &msgid )) + != LDAP_SUCCESS ) { + if ( res != NULL ) { + *res = NULL; + } + return( err ); + } + + if ( ldap_result( ld, msgid, 1, localtimeoutp, res ) == -1 ) { + /* + * Error. ldap_result() sets *res to NULL for us. + */ + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) { + (void) ldap_abandon( ld, msgid ); + err = LDAP_TIMEOUT; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + if ( res != NULL ) { + *res = NULL; + } + return( err ); + } + + return( ldap_result2error( ld, *res, 0 ) ); +} diff --git a/ldap/c-sdk/libraries/libldap/setoption.c b/ldap/c-sdk/libraries/libldap/setoption.c new file mode 100644 index 0000000000..5c4d753f5a --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/setoption.c @@ -0,0 +1,411 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * setoption.c - ldap_set_option implementation + */ + +#include "ldap-int.h" + +#define LDAP_SETCLR_BITOPT( ld, bit, optdata ) \ + if ( optdata != NULL ) { \ + (ld)->ld_options |= bit; \ + } else { \ + (ld)->ld_options &= ~bit; \ + } + + +int +LDAP_CALL +ldap_set_option( LDAP *ld, int option, const void *optdata ) +{ + int rc, i; + char *matched, *errstr; + + /* + * if ld is NULL, arrange to modify our default settings + */ + if ( ld == NULL ) { + if ( !nsldapi_initialized ) { + nsldapi_initialize_defaults(); + } + ld = &nsldapi_ld_defaults; + } + + /* + * process global options (not associated with an LDAP session handle) + */ + if ( option == LDAP_OPT_MEMALLOC_FN_PTRS ) { + struct lber_memalloc_fns memalloc_fns; + + /* set libldap ones via a struct copy */ + nsldapi_memalloc_fns = *((struct ldap_memalloc_fns *)optdata); + + /* also set liblber memory allocation callbacks */ + memalloc_fns.lbermem_malloc = + nsldapi_memalloc_fns.ldapmem_malloc; + memalloc_fns.lbermem_calloc = + nsldapi_memalloc_fns.ldapmem_calloc; + memalloc_fns.lbermem_realloc = + nsldapi_memalloc_fns.ldapmem_realloc; + memalloc_fns.lbermem_free = + nsldapi_memalloc_fns.ldapmem_free; + if ( ber_set_option( NULL, LBER_OPT_MEMALLOC_FN_PTRS, + &memalloc_fns ) != 0 ) { + return( -1 ); + } + + return( 0 ); + } + /* + * LDAP_OPT_DEBUG_LEVEL is global + */ + if (LDAP_OPT_DEBUG_LEVEL == option) + { +#ifdef LDAP_DEBUG + ldap_debug = *((int *) optdata); +#endif + return 0; + } + + /* + * process options that are associated with an LDAP session handle + */ + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); /* punt */ + } + + rc = 0; + if ( ld != &nsldapi_ld_defaults + && option != LDAP_OPT_EXTRA_THREAD_FN_PTRS + && option != LDAP_OPT_THREAD_FN_PTRS ) { + LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); + } + switch( option ) { + /* options that can be turned on and off */ +#ifdef LDAP_DNS + case LDAP_OPT_DNS: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_DNS, optdata ); + break; +#endif + + case LDAP_OPT_REFERRALS: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_REFERRALS, optdata ); + break; + + case LDAP_OPT_SSL: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_SSL, optdata ); + break; + + case LDAP_OPT_RESTART: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_RESTART, optdata ); + break; + + case LDAP_OPT_RECONNECT: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_RECONNECT, optdata ); + break; + + case LDAP_OPT_NOREBIND: + LDAP_SETCLR_BITOPT( ld, LDAP_BITOPT_NOREBIND, optdata ); + break; + +#ifdef LDAP_ASYNC_IO + case LDAP_OPT_ASYNC_CONNECT: + LDAP_SETCLR_BITOPT(ld, LDAP_BITOPT_ASYNC, optdata ); + break; +#endif /* LDAP_ASYNC_IO */ + + /* fields in the LDAP structure */ + case LDAP_OPT_DEREF: + ld->ld_deref = *((int *) optdata); + break; + case LDAP_OPT_SIZELIMIT: + ld->ld_sizelimit = *((int *) optdata); + break; + case LDAP_OPT_TIMELIMIT: + ld->ld_timelimit = *((int *) optdata); + break; + case LDAP_OPT_REFERRAL_HOP_LIMIT: + ld->ld_refhoplimit = *((int *) optdata); + break; + case LDAP_OPT_PROTOCOL_VERSION: + ld->ld_version = *((int *) optdata); + if ( ld->ld_defconn != NULL ) { /* also set in default conn. */ + ld->ld_defconn->lconn_version = ld->ld_version; + } + break; + case LDAP_OPT_SERVER_CONTROLS: + /* nsldapi_dup_controls returns -1 and sets lderrno on error */ + rc = nsldapi_dup_controls( ld, &ld->ld_servercontrols, + (LDAPControl **)optdata ); + break; + case LDAP_OPT_CLIENT_CONTROLS: + /* nsldapi_dup_controls returns -1 and sets lderrno on error */ + rc = nsldapi_dup_controls( ld, &ld->ld_clientcontrols, + (LDAPControl **)optdata ); + break; + + /* rebind proc */ + case LDAP_OPT_REBIND_FN: + ld->ld_rebind_fn = (LDAP_REBINDPROC_CALLBACK *) optdata; + break; + case LDAP_OPT_REBIND_ARG: + ld->ld_rebind_arg = (void *) optdata; + break; + + /* i/o function pointers */ + case LDAP_OPT_IO_FN_PTRS: + if (( rc = nsldapi_install_compat_io_fns( ld, + (struct ldap_io_fns *)optdata )) != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + rc = -1; + } + break; + + /* extended i/o function pointers */ + case LDAP_X_OPT_EXTIO_FN_PTRS: + /* denotes use of old iofns struct (no writev) */ + if (((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_size == LDAP_X_EXTIO_FNS_SIZE_REV0) { + ld->ld_extio_size = LDAP_X_EXTIO_FNS_SIZE; + ld->ld_extclose_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_close; + ld->ld_extconnect_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_connect; + ld->ld_extread_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_read; + ld->ld_extwrite_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_write; + ld->ld_extpoll_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_poll; + ld->ld_extnewhandle_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_newhandle; + ld->ld_extdisposehandle_fn = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_disposehandle; + ld->ld_ext_session_arg = ((struct ldap_x_ext_io_fns_rev0 *) optdata)->lextiof_session_arg; + ld->ld_extwritev_fn = NULL; + if ( ber_sockbuf_set_option( ld->ld_sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS, + &(ld->ld_ext_io_fns) ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + rc = -1; + break; + } + } + else { + /* struct copy */ + ld->ld_ext_io_fns = *((struct ldap_x_ext_io_fns *) optdata); + } + if (( rc = nsldapi_install_lber_extiofns( ld, ld->ld_sbp )) + != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + rc = -1; + } + break; + + /* set Socket Arg in extended socket i/o functions*/ + case LDAP_X_OPT_SOCKETARG: + if ( ber_sockbuf_set_option( ld->ld_sbp, + LBER_SOCKBUF_OPT_SOCK_ARG, (void *)optdata ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + rc = -1; + } + + break; + + /* thread function pointers */ + case LDAP_OPT_THREAD_FN_PTRS: + /* + * It is only safe to set the thread function pointers + * when one thread is using the LDAP session handle. + */ + /* free existing mutexes (some are allocated by ldap_init()) */ + nsldapi_mutex_free_all( ld ); + + /* struct copy */ + ld->ld_thread = *((struct ldap_thread_fns *) optdata); + + /* allocate new mutexes */ + nsldapi_mutex_alloc_all( ld ); + + /* LDAP_OPTION_LOCK was never locked... so just return */ + return (rc); + + /* extra thread function pointers */ + case LDAP_OPT_EXTRA_THREAD_FN_PTRS: + /* The extra thread funcs will only pick up the threadid */ + ld->ld_thread2 = *((struct ldap_extra_thread_fns *) optdata); + + /* Reset the rest of the structure preserving the threadid fn */ + ld->ld_mutex_trylock_fn = (LDAP_TF_MUTEX_TRYLOCK_CALLBACK *)NULL; + ld->ld_sema_alloc_fn = (LDAP_TF_SEMA_ALLOC_CALLBACK *) NULL; + ld->ld_sema_free_fn = (LDAP_TF_SEMA_FREE_CALLBACK *) NULL; + ld->ld_sema_wait_fn = (LDAP_TF_SEMA_WAIT_CALLBACK *) NULL; + ld->ld_sema_post_fn = (LDAP_TF_SEMA_POST_CALLBACK *) NULL; + + /* We assume that only one thread is active when replacing */ + /* the threadid function. We will now proceed and reset all */ + /* of the threadid/refcounts */ + for( i=0; ild_mutex_threadid[i] = (void *) -1; + ld->ld_mutex_refcnt[i] = 0; + } + + /* LDAP_OPTION_LOCK was never locked... so just return */ + return (rc); + + /* DNS function pointers */ + case LDAP_OPT_DNS_FN_PTRS: + /* struct copy */ + ld->ld_dnsfn = *((struct ldap_dns_fns *) optdata); + break; + + /* cache function pointers */ + case LDAP_OPT_CACHE_FN_PTRS: + /* struct copy */ + ld->ld_cache = *((struct ldap_cache_fns *) optdata); + break; + case LDAP_OPT_CACHE_STRATEGY: + ld->ld_cache_strategy = *((int *) optdata); + break; + case LDAP_OPT_CACHE_ENABLE: + ld->ld_cache_on = *((int *) optdata); + break; + + case LDAP_OPT_ERROR_NUMBER: + LDAP_GET_LDERRNO( ld, &matched, &errstr ); + matched = nsldapi_strdup( matched ); + errstr = nsldapi_strdup( errstr ); + LDAP_SET_LDERRNO( ld, *((int *) optdata), matched, errstr ); + break; + + case LDAP_OPT_ERROR_STRING: + rc = LDAP_GET_LDERRNO( ld, &matched, NULL ); + matched = nsldapi_strdup( matched ); + LDAP_SET_LDERRNO( ld, rc, matched, + nsldapi_strdup((char *) optdata)); + rc = LDAP_SUCCESS; + break; + + case LDAP_OPT_MATCHED_DN: + rc = LDAP_GET_LDERRNO( ld, NULL, &errstr ); + errstr = nsldapi_strdup( errstr ); + LDAP_SET_LDERRNO( ld, rc, + nsldapi_strdup((char *) optdata), errstr ); + rc = LDAP_SUCCESS; + break; + + case LDAP_OPT_PREFERRED_LANGUAGE: + if ( NULL != ld->ld_preferred_language ) { + NSLDAPI_FREE(ld->ld_preferred_language); + } + ld->ld_preferred_language = nsldapi_strdup((char *) optdata); + break; + + case LDAP_OPT_HOST_NAME: + if ( NULL != ld->ld_defhost ) { + NSLDAPI_FREE(ld->ld_defhost); + } + ld->ld_defhost = nsldapi_strdup((char *) optdata); + break; + + case LDAP_X_OPT_CONNECT_TIMEOUT: + ld->ld_connect_timeout = *((int *) optdata); + break; + +#ifdef LDAP_SASLIO_HOOKS + /* SASL options */ + case LDAP_OPT_X_SASL_MECH: + NSLDAPI_FREE(ld->ld_def_sasl_mech); + ld->ld_def_sasl_mech = nsldapi_strdup((char *) optdata); + break; + case LDAP_OPT_X_SASL_REALM: + NSLDAPI_FREE(ld->ld_def_sasl_realm); + ld->ld_def_sasl_realm = nsldapi_strdup((char *) optdata); + break; + case LDAP_OPT_X_SASL_AUTHCID: + NSLDAPI_FREE(ld->ld_def_sasl_authcid); + ld->ld_def_sasl_authcid = nsldapi_strdup((char *) optdata); + break; + case LDAP_OPT_X_SASL_AUTHZID: + NSLDAPI_FREE(ld->ld_def_sasl_authzid); + ld->ld_def_sasl_authzid = nsldapi_strdup((char *) optdata); + break; + case LDAP_OPT_X_SASL_SSF_EXTERNAL: + { + int sc; + sasl_ssf_t extprops; + sasl_conn_t *ctx; + if( ld->ld_defconn == NULL ) { + return -1; + } + ctx = (sasl_conn_t *)(ld->ld_defconn->lconn_sasl_ctx); + if ( ctx == NULL ) { + return -1; + } + memset(&extprops, 0L, sizeof(extprops)); + extprops = * ((sasl_ssf_t *) optdata); + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); + if ( sc != SASL_OK ) { + return -1; + } + } + break; + case LDAP_OPT_X_SASL_SECPROPS: + { + int sc; + sc = nsldapi_sasl_secprops( (char *) optdata, + &ld->ld_sasl_secprops ); + return sc == LDAP_SUCCESS ? 0 : -1; + } + break; + case LDAP_OPT_X_SASL_SSF_MIN: + ld->ld_sasl_secprops.min_ssf = *((sasl_ssf_t *) optdata); + break; + case LDAP_OPT_X_SASL_SSF_MAX: + ld->ld_sasl_secprops.max_ssf = *((sasl_ssf_t *) optdata); + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + ld->ld_sasl_secprops.maxbufsize = *((sasl_ssf_t *) optdata); + break; + case LDAP_OPT_X_SASL_SSF: /* read only */ + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + rc = -1; + break; +#endif + + default: + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + rc = -1; + } + + if ( ld != &nsldapi_ld_defaults ) { + LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); + } + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libldap/sort.c b/ldap/c-sdk/libraries/libldap/sort.c new file mode 100644 index 0000000000..8a716c818b --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/sort.c @@ -0,0 +1,355 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* + * sort.c: LDAP library entry and value sort routines + */ + +#include "ldap-int.h" + +/* This xp_qsort fixes a memory problem (ABR) on Solaris for the client. + * Server is welcome to use it too, but I wasn't sure if it + * would be ok to use XP code here. -slamm + * + * We don't want to require use of libxp when linking with libldap, so + * I'll leave use of xp_qsort as a MOZILLA_CLIENT-only thing for now. --mcs + */ +#if defined(MOZILLA_CLIENT) && defined(SOLARIS) +#include "xp_qsort.h" +#else +#define XP_QSORT qsort +#endif + +typedef struct keycmp { + void *kc_arg; + LDAP_KEYCMP_CALLBACK *kc_cmp; +} keycmp_t; + +typedef struct keything { + keycmp_t *kt_cmp; + const struct berval *kt_key; + LDAPMessage *kt_msg; +} keything_t; + +static int LDAP_C LDAP_CALLBACK +ldapi_keycmp( const void *Lv, const void *Rv ) +{ + auto keything_t **L = (keything_t**)Lv; + auto keything_t **R = (keything_t**)Rv; + auto keycmp_t *cmp = (*L)->kt_cmp; + return cmp->kc_cmp( cmp->kc_arg, (*L)->kt_key, (*R)->kt_key ); +} + +int +LDAP_CALL +ldap_keysort_entries( + LDAP *ld, + LDAPMessage **chain, + void *arg, + LDAP_KEYGEN_CALLBACK *gen, + LDAP_KEYCMP_CALLBACK *cmp, + LDAP_KEYFREE_CALLBACK *fre) +{ + size_t count, i; + keycmp_t kc = {0}; + keything_t **kt; + LDAPMessage *e, *last; + LDAPMessage **ep; + int scount; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) + || chain == NULL || cmp == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + scount = ldap_count_entries( ld, *chain ); + + if (scount < 0) { /* error */ + return( LDAP_PARAM_ERROR ); + } + + count = scount; + + if (count < 2) { /* nothing to sort */ + return( 0 ); + } + + kt = (keything_t**)NSLDAPI_MALLOC( count * (sizeof(keything_t*) + sizeof(keything_t)) ); + if ( kt == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + for ( i = 0; i < count; i++ ) { + kt[i] = i + (keything_t*)(kt + count); + } + kc.kc_arg = arg; + kc.kc_cmp = cmp; + + for ( e = *chain, i = 0; i < count; i++, e = e->lm_chain ) { + kt[i]->kt_msg = e; + kt[i]->kt_cmp = &kc; + kt[i]->kt_key = gen( arg, ld, e ); + if ( kt[i]->kt_key == NULL ) { + if ( fre ) while ( i-- > 0 ) fre( arg, kt[i]->kt_key ); + NSLDAPI_FREE( (char*)kt ); + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + } + last = e; + + XP_QSORT( (void*)kt, count, (size_t)sizeof(keything_t*), ldapi_keycmp ); + + ep = chain; + for ( i = 0; i < count; i++ ) { + *ep = kt[i]->kt_msg; + ep = &(*ep)->lm_chain; + if ( fre ) fre( arg, kt[i]->kt_key ); + } + *ep = last; + NSLDAPI_FREE( (char*)kt ); + return( 0 ); +} + + +struct entrything { + char **et_vals; + LDAPMessage *et_msg; +}; + +typedef int (LDAP_C LDAP_CALLBACK LDAP_CHARCMP_CALLBACK)(char*, char*); +typedef int (LDAP_C LDAP_CALLBACK LDAP_VOIDCMP_CALLBACK)(const void*, + const void*); + +static LDAP_CHARCMP_CALLBACK *et_cmp_fn; +static LDAP_VOIDCMP_CALLBACK et_cmp; + +int +LDAP_C +LDAP_CALLBACK +ldap_sort_strcasecmp( + const char **a, + const char **b +) +{ + /* XXXceb + * I am not 100% sure this is the way this should be handled. + * For now we will return a 0 on invalid. + */ + if (NULL == a || NULL == b) + return (0); + return( strcasecmp( (char *)*a, (char *)*b ) ); +} + +static int +LDAP_C +LDAP_CALLBACK +et_cmp( + const void *aa, + const void *bb +) +{ + int i, rc; + struct entrything *a = (struct entrything *)aa; + struct entrything *b = (struct entrything *)bb; + + if ( a->et_vals == NULL && b->et_vals == NULL ) + return( 0 ); + if ( a->et_vals == NULL ) + return( -1 ); + if ( b->et_vals == NULL ) + return( 1 ); + + for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) { + if ( (rc = (*et_cmp_fn)( a->et_vals[i], b->et_vals[i] )) + != 0 ) { + return( rc ); + } + } + + if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL ) + return( 0 ); + if ( a->et_vals[i] == NULL ) + return( -1 ); + return( 1 ); +} + +int +LDAP_CALL +ldap_multisort_entries( + LDAP *ld, + LDAPMessage **chain, + char **attr, /* NULL => sort by DN */ + LDAP_CMP_CALLBACK *cmp +) +{ + int i, count; + struct entrything *et; + LDAPMessage *e, *last; + LDAPMessage **ep; + int scount; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) + || chain == NULL || cmp == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + scount = ldap_count_entries( ld, *chain ); + + if (scount < 0) { /* error, usually with bad ld or malloc */ + return( LDAP_PARAM_ERROR ); + } + + count = scount; + + if (count < 2) { /* nothing to sort */ + return( 0 ); + } + + if ( (et = (struct entrything *)NSLDAPI_MALLOC( count * + sizeof(struct entrything) )) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + + e = *chain; + for ( i = 0; i < count; i++ ) { + et[i].et_msg = e; + et[i].et_vals = NULL; + if ( attr == NULL ) { + char *dn; + + dn = ldap_get_dn( ld, e ); + et[i].et_vals = ldap_explode_dn( dn, 1 ); + NSLDAPI_FREE( dn ); + } else { + int attrcnt; + char **vals; + + for ( attrcnt = 0; attr[attrcnt] != NULL; attrcnt++ ) { + vals = ldap_get_values( ld, e, attr[attrcnt] ); + if ( ldap_charray_merge( &(et[i].et_vals), vals ) + != 0 ) { + int j; + + /* XXX risky: ldap_value_free( vals ); */ + for ( j = 0; j <= i; j++ ) + ldap_value_free( et[j].et_vals ); + NSLDAPI_FREE( (char *) et ); + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, + NULL ); + return( -1 ); + } + if ( vals != NULL ) { + NSLDAPI_FREE( (char *)vals ); + } + } + } + + e = e->lm_chain; + } + last = e; + + et_cmp_fn = (LDAP_CHARCMP_CALLBACK *)cmp; + XP_QSORT( (void *) et, (size_t) count, + (size_t) sizeof(struct entrything), et_cmp ); + + ep = chain; + for ( i = 0; i < count; i++ ) { + *ep = et[i].et_msg; + ep = &(*ep)->lm_chain; + + ldap_value_free( et[i].et_vals ); + } + *ep = last; + NSLDAPI_FREE( (char *) et ); + + return( 0 ); +} + +int +LDAP_CALL +ldap_sort_entries( + LDAP *ld, + LDAPMessage **chain, + char *attr, /* NULL => sort by DN */ + LDAP_CMP_CALLBACK *cmp +) +{ + char *attrs[2]; + + attrs[0] = attr; + attrs[1] = NULL; + return( ldap_multisort_entries( ld, chain, attr ? attrs : NULL, cmp ) ); +} + +int +LDAP_CALL +ldap_sort_values( + LDAP *ld, + char **vals, + LDAP_VALCMP_CALLBACK *cmp +) +{ + int nel; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || cmp == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + if ( NULL == vals) + { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + for ( nel = 0; vals[nel] != NULL; nel++ ) + ; /* NULL */ + + XP_QSORT( vals, nel, sizeof(char *), (LDAP_VOIDCMP_CALLBACK *)cmp ); + + return( LDAP_SUCCESS ); +} diff --git a/ldap/c-sdk/libraries/libldap/sortctrl.c b/ldap/c-sdk/libraries/libldap/sortctrl.c new file mode 100644 index 0000000000..585e2fd3fa --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/sortctrl.c @@ -0,0 +1,438 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "ldap-int.h" + +/* ldap_create_sort_control: + + Parameters are + + ld LDAP pointer to the desired connection + + sortKeyList an array of sortkeys + + ctl_iscritical Indicates whether the control is critical of not. If + this field is non-zero, the operation will only be car- + ried out if the control is recognized by the server + and/or client + + ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_sort_control ( + LDAP *ld, + LDAPsortkey **sortKeyList, + const char ctl_iscritical, + LDAPControl **ctrlp +) +{ + BerElement *ber; + int i, rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( sortKeyList == NULL || ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + /* create a ber package to hold the controlValue */ + if ( ( nsldapi_alloc_ber_with_options( ld, &ber ) ) != LDAP_SUCCESS ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* encode the start of the sequence of sequences into the ber */ + if ( ber_printf( ber, "{" ) == -1 ) { + goto encoding_error_exit; + } + + /* the sort control value will be encoded as a sequence of sequences + which are each encoded as one of the following: {s} or {sts} or {stb} or {ststb} + since the orderingRule and reverseOrder flag are both optional */ + for ( i = 0; sortKeyList[i] != NULL; i++ ) { + + /* encode the attributeType into the ber */ + if ( ber_printf( ber, "{s", (sortKeyList[i])->sk_attrtype ) + == -1 ) { + goto encoding_error_exit; + } + + /* encode the optional orderingRule into the ber */ + if ( (sortKeyList[i])->sk_matchruleoid != NULL ) { + if ( ber_printf( ber, "ts", LDAP_TAG_SK_MATCHRULE, + (sortKeyList[i])->sk_matchruleoid ) + == -1 ) { + goto encoding_error_exit; + } + } + + /* Encode the optional reverseOrder flag into the ber. */ + /* If the flag is false, it should be absent. */ + if ( (sortKeyList[i])->sk_reverseorder ) { + if ( ber_printf( ber, "tb}", LDAP_TAG_SK_REVERSE, + (sortKeyList[i])->sk_reverseorder ) == -1 ) { + goto encoding_error_exit; + } + } else { + if ( ber_printf( ber, "}" ) == -1 ) { + goto encoding_error_exit; + } + } + } + + /* encode the end of the sequence of sequences into the ber */ + if ( ber_printf( ber, "}" ) == -1 ) { + goto encoding_error_exit; + } + + rc = nsldapi_build_control( LDAP_CONTROL_SORTREQUEST, ber, 1, + ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + +encoding_error_exit: + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); +} + +/* ldap_parse_sort_control: + + Parameters are + + ld LDAP pointer to the desired connection + + ctrlp An array of controls obtained from calling + ldap_parse_result on the set of results returned by + the server + + result the address of a place to put the result code + + attribute the address of a place to put the name of the + attribute which cause the operation to fail, optionally + returned by the server */ + +int +LDAP_CALL +ldap_parse_sort_control ( + LDAP *ld, + LDAPControl **ctrlp, + ber_int_t *result, + char **attribute +) +{ + BerElement *ber; + int i, foundSortControl; + LDAPControl *sortCtrlp; + ber_len_t len; + ber_tag_t tag; + char *attr; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || result == NULL || + attribute == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + + /* find the sortControl in the list of controls if it exists */ + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + foundSortControl = 0; + for ( i = 0; (( ctrlp[i] != NULL ) && ( !foundSortControl )); i++ ) { + foundSortControl = !strcmp( ctrlp[i]->ldctl_oid, LDAP_CONTROL_SORTRESPONSE ); + } + if ( !foundSortControl ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } else { + /* let local var point to the sortControl */ + sortCtrlp = ctrlp[i-1]; + } + + /* allocate a Ber element with the contents of the sort_control's struct berval */ + if ( ( ber = ber_init( &sortCtrlp->ldctl_value ) ) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* decode the result from the Berelement */ + if ( ber_scanf( ber, "{i", result ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + + /* if the server returned one, decode the attribute from the Ber element */ + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SR_ATTRTYPE ) { + if ( ber_scanf( ber, "ta", &tag, &attr ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + *attribute = attr; + } else { + *attribute = NULL; + } + + if ( ber_scanf( ber, "}" ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + + /* the ber encoding is no longer needed */ + ber_free(ber,1); + + return( LDAP_SUCCESS ); +} + +/* Routines for the manipulation of string-representations of sort control keylists */ + +static int count_tokens(const char *s) +{ + int count = 0; + const char *p = s; + int whitespace = 1; + /* Loop along the string counting the number of times we see the + * beginning of non-whitespace. This tells us + * the number of tokens in the string + */ + while (*p != '\0') { + if (whitespace) { + if (!isspace(*p)) { + whitespace = 0; + count++; + } + } else { + if (isspace(*p)) { + whitespace = 1; + } + } + p++; + } + return count; +} + + +static int read_next_token(const char **s,LDAPsortkey **key) +{ + char c = 0; + const char *pos = *s; + int retval = 0; + LDAPsortkey *new_key = NULL; + + const char *matchrule_source = NULL; + int matchrule_size = 0; + const char *attrdesc_source = NULL; + int attrdesc_size = 0; + int reverse = 0; + + int state = 0; + + while ( ((c = *pos++) != '\0') && (state != 4) ) { + switch (state) { + case 0: + /* case where we've not seen the beginning of the attr yet */ + /* If we still see whitespace, nothing to do */ + if (!isspace(c)) { + /* Otherwise, something to look at */ + /* Is it a minus sign ? */ + if ('-' == c) { + reverse = 1; + } else { + attrdesc_source = pos - 1; + state = 1; + } + } + break; + case 1: + /* case where we've seen the beginning of the attr, but not the end */ + /* Is this char either whitespace or a ';' ? */ + if ( isspace(c) || (':' == c)) { + attrdesc_size = (pos - attrdesc_source) - 1; + if (':' == c) { + state = 2; + } else { + state = 4; + } + } + break; + case 2: + /* case where we've seen the end of the attr and want the beginning of match rule */ + if (!isspace(c)) { + matchrule_source = pos - 1; + state = 3; + } else { + state = 4; + } + break; + case 3: + /* case where we've seen the beginning of match rule and want to find the end */ + if (isspace(c)) { + matchrule_size = (pos - matchrule_source) - 1; + state = 4; + } + break; + default: + break; + } + } + + if (3 == state) { + /* means we fell off the end of the string looking for the end of the marching rule */ + matchrule_size = (pos - matchrule_source) - 1; + } + + if (1 == state) { + /* means we fell of the end of the string looking for the end of the attribute */ + attrdesc_size = (pos - attrdesc_source) - 1; + } + + if (NULL == attrdesc_source) { + /* Didn't find anything */ + return -1; + } + + new_key = (LDAPsortkey*)NSLDAPI_MALLOC(sizeof(LDAPsortkey)); + if (0 == new_key) { + return LDAP_NO_MEMORY; + } + + /* Allocate the strings */ + new_key->sk_attrtype = (char *)NSLDAPI_MALLOC(attrdesc_size + 1); + if (NULL != matchrule_source) { + new_key->sk_matchruleoid = (char *)NSLDAPI_MALLOC( + matchrule_size + 1); + } else { + new_key->sk_matchruleoid = NULL; + } + /* Copy over the strings */ + memcpy(new_key->sk_attrtype,attrdesc_source,attrdesc_size); + *(new_key->sk_attrtype + attrdesc_size) = '\0'; + if (NULL != matchrule_source) { + memcpy(new_key->sk_matchruleoid,matchrule_source,matchrule_size); + *(new_key->sk_matchruleoid + matchrule_size) = '\0'; + } + + new_key->sk_reverseorder = reverse; + + *s = pos - 1; + *key = new_key; + return retval; +} + +int +LDAP_CALL +ldap_create_sort_keylist ( + LDAPsortkey ***sortKeyList, + const char *string_rep +) +{ + int count = 0; + LDAPsortkey **pointer_array = NULL; + const char *current_position = NULL; + int retval = 0; + int i = 0; + + /* Figure out how many there are */ + if (NULL == string_rep) { + return LDAP_PARAM_ERROR; + } + if (NULL == sortKeyList) { + return LDAP_PARAM_ERROR; + } + count = count_tokens(string_rep); + if (0 == count) { + *sortKeyList = NULL; + return LDAP_PARAM_ERROR; + } + /* Allocate enough memory for the pointers */ + pointer_array = (LDAPsortkey**)NSLDAPI_MALLOC(sizeof(LDAPsortkey*) + * (count + 1) ); + if (NULL == pointer_array) { + return LDAP_NO_MEMORY; + } + /* Now walk along the string, allocating and filling in the LDAPsearchkey structure */ + current_position = string_rep; + + for (i = 0; i < count; i++) { + if (0 != (retval = read_next_token(¤t_position,&(pointer_array[i])))) { + pointer_array[count] = NULL; + ldap_free_sort_keylist(pointer_array); + *sortKeyList = NULL; + return retval; + } + } + pointer_array[count] = NULL; + *sortKeyList = pointer_array; + return LDAP_SUCCESS; +} + +void +LDAP_CALL +ldap_free_sort_keylist ( + LDAPsortkey **sortKeyList +) +{ + LDAPsortkey *this_one = NULL; + int i = 0; + + if ( NULL == sortKeyList ) { + return; + } + + /* Walk down the list freeing the LDAPsortkey structures */ + for (this_one = sortKeyList[0]; this_one ; this_one = sortKeyList[++i]) { + /* Free the strings, if present */ + if (NULL != this_one->sk_attrtype) { + NSLDAPI_FREE(this_one->sk_attrtype); + } + if (NULL != this_one->sk_matchruleoid) { + NSLDAPI_FREE(this_one->sk_matchruleoid); + } + NSLDAPI_FREE(this_one); + } + /* Free the pointer list */ + NSLDAPI_FREE(sortKeyList); +} diff --git a/ldap/c-sdk/libraries/libldap/srchpref.c b/ldap/c-sdk/libraries/libldap/srchpref.c new file mode 100644 index 0000000000..a3025afd39 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/srchpref.c @@ -0,0 +1,434 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1993, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + * + */ +/* + * searchpref.c: search preferences library routines for LDAP clients + */ + +#include "ldap-int.h" +#include "srchpref.h" + +static void free_searchobj( struct ldap_searchobj *so ); +static int read_next_searchobj( char **bufp, long *blenp, + struct ldap_searchobj **sop, int soversion ); + + +static char *sobjoptions[] = { + "internal", + NULL +}; + + +static unsigned long sobjoptvals[] = { + LDAP_SEARCHOBJ_OPT_INTERNAL, +}; + + +int +LDAP_CALL +ldap_init_searchprefs( char *file, struct ldap_searchobj **solistp ) +{ + FILE *fp; + char *buf; + long rlen, len; + int rc, eof; + + if (( fp = NSLDAPI_FOPEN( file, "r" )) == NULL ) { + return( LDAP_SEARCHPREF_ERR_FILE ); + } + + if ( fseek( fp, 0L, SEEK_END ) != 0 ) { /* move to end to get len */ + fclose( fp ); + return( LDAP_SEARCHPREF_ERR_FILE ); + } + + len = ftell( fp ); + + if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { /* back to start of file */ + fclose( fp ); + return( LDAP_SEARCHPREF_ERR_FILE ); + } + + if (( buf = NSLDAPI_MALLOC( (size_t)len )) == NULL ) { + fclose( fp ); + return( LDAP_SEARCHPREF_ERR_MEM ); + } + + rlen = fread( buf, 1, (size_t)len, fp ); + eof = feof( fp ); + fclose( fp ); + + if ( rlen != len && !eof ) { /* error: didn't get the whole file */ + NSLDAPI_FREE( buf ); + return( LDAP_SEARCHPREF_ERR_FILE ); + } + + rc = ldap_init_searchprefs_buf( buf, rlen, solistp ); + NSLDAPI_FREE( buf ); + + return( rc ); +} + + +int +LDAP_CALL +ldap_init_searchprefs_buf( char *buf, long buflen, + struct ldap_searchobj **solistp ) +{ + int rc = 0, version; + char **toks; + struct ldap_searchobj *prevso, *so; + + *solistp = prevso = NULLSEARCHOBJ; + + if ( nsldapi_next_line_tokens( &buf, &buflen, &toks ) != 2 || + strcasecmp( toks[ 0 ], "version" ) != 0 ) { + nsldapi_free_strarray( toks ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + version = atoi( toks[ 1 ] ); + nsldapi_free_strarray( toks ); + if ( version != LDAP_SEARCHPREF_VERSION && + version != LDAP_SEARCHPREF_VERSION_ZERO ) { + return( LDAP_SEARCHPREF_ERR_VERSION ); + } + + while ( buflen > 0 && ( rc = read_next_searchobj( &buf, &buflen, &so, + version )) == 0 && so != NULLSEARCHOBJ ) { + if ( prevso == NULLSEARCHOBJ ) { + *solistp = so; + } else { + prevso->so_next = so; + } + prevso = so; + } + + if ( rc != 0 ) { + ldap_free_searchprefs( *solistp ); + } + + return( rc ); +} + + + +void +LDAP_CALL +ldap_free_searchprefs( struct ldap_searchobj *solist ) +{ + struct ldap_searchobj *so, *nextso; + + if ( solist != NULL ) { + for ( so = solist; so != NULL; so = nextso ) { + nextso = so->so_next; + free_searchobj( so ); + } + } + /* XXX XXX need to do some work here */ +} + + +static void +free_searchobj( struct ldap_searchobj *so ) +{ + if ( so != NULL ) { + if ( so->so_objtypeprompt != NULL ) { + NSLDAPI_FREE( so->so_objtypeprompt ); + } + if ( so->so_prompt != NULL ) { + NSLDAPI_FREE( so->so_prompt ); + } + if ( so->so_filterprefix != NULL ) { + NSLDAPI_FREE( so->so_filterprefix ); + } + if ( so->so_filtertag != NULL ) { + NSLDAPI_FREE( so->so_filtertag ); + } + if ( so->so_defaultselectattr != NULL ) { + NSLDAPI_FREE( so->so_defaultselectattr ); + } + if ( so->so_defaultselecttext != NULL ) { + NSLDAPI_FREE( so->so_defaultselecttext ); + } + if ( so->so_salist != NULL ) { + struct ldap_searchattr *sa, *nextsa; + for ( sa = so->so_salist; sa != NULL; sa = nextsa ) { + nextsa = sa->sa_next; + if ( sa->sa_attrlabel != NULL ) { + NSLDAPI_FREE( sa->sa_attrlabel ); + } + if ( sa->sa_attr != NULL ) { + NSLDAPI_FREE( sa->sa_attr ); + } + if ( sa->sa_selectattr != NULL ) { + NSLDAPI_FREE( sa->sa_selectattr ); + } + if ( sa->sa_selecttext != NULL ) { + NSLDAPI_FREE( sa->sa_selecttext ); + } + NSLDAPI_FREE( sa ); + } + } + if ( so->so_smlist != NULL ) { + struct ldap_searchmatch *sm, *nextsm; + for ( sm = so->so_smlist; sm != NULL; sm = nextsm ) { + nextsm = sm->sm_next; + if ( sm->sm_matchprompt != NULL ) { + NSLDAPI_FREE( sm->sm_matchprompt ); + } + if ( sm->sm_filter != NULL ) { + NSLDAPI_FREE( sm->sm_filter ); + } + NSLDAPI_FREE( sm ); + } + } + NSLDAPI_FREE( so ); + } +} + + + +struct ldap_searchobj * +LDAP_CALL +ldap_first_searchobj( struct ldap_searchobj *solist ) +{ + return( solist ); +} + + +struct ldap_searchobj * +LDAP_CALL +ldap_next_searchobj( struct ldap_searchobj *solist, struct ldap_searchobj *so ) +{ + return( so == NULLSEARCHOBJ ? so : so->so_next ); +} + + + +static int +read_next_searchobj( char **bufp, long *blenp, struct ldap_searchobj **sop, + int soversion ) +{ + int i, j, tokcnt; + char **toks; + struct ldap_searchobj *so; + struct ldap_searchattr **sa; + struct ldap_searchmatch **sm; + + *sop = NULL; + + /* + * Object type prompt comes first + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + return( tokcnt == 0 ? 0 : LDAP_SEARCHPREF_ERR_SYNTAX ); + } + + if (( so = (struct ldap_searchobj *)NSLDAPI_CALLOC( 1, + sizeof( struct ldap_searchobj ))) == NULL ) { + nsldapi_free_strarray( toks ); + return( LDAP_SEARCHPREF_ERR_MEM ); + } + so->so_objtypeprompt = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * if this is post-version zero, options come next + */ + if ( soversion > LDAP_SEARCHPREF_VERSION_ZERO ) { + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) < 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + for ( i = 0; toks[ i ] != NULL; ++i ) { + for ( j = 0; sobjoptions[ j ] != NULL; ++j ) { + if ( strcasecmp( toks[ i ], sobjoptions[ j ] ) == 0 ) { + so->so_options |= sobjoptvals[ j ]; + } + } + } + nsldapi_free_strarray( toks ); + } + + /* + * "Fewer choices" prompt is next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + so->so_prompt = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * Filter prefix for "More Choices" searching is next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + so->so_filterprefix = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * "Fewer Choices" filter tag comes next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + so->so_filtertag = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * Selection (disambiguation) attribute comes next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + so->so_defaultselectattr = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * Label for selection (disambiguation) attribute + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + so->so_defaultselecttext = toks[ 0 ]; + NSLDAPI_FREE( (char *)toks ); + + /* + * Search scope is next + */ + if (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) != 1 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + if ( !strcasecmp(toks[ 0 ], "subtree" )) { + so->so_defaultscope = LDAP_SCOPE_SUBTREE; + } else if ( !strcasecmp(toks[ 0 ], "onelevel" )) { + so->so_defaultscope = LDAP_SCOPE_ONELEVEL; + } else if ( !strcasecmp(toks[ 0 ], "base" )) { + so->so_defaultscope = LDAP_SCOPE_BASE; + } else { + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + nsldapi_free_strarray( toks ); + + + /* + * "More Choices" search option list comes next + */ + sa = &( so->so_salist ); + while (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) > 0 ) { + if ( tokcnt < 5 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + if (( *sa = ( struct ldap_searchattr * )NSLDAPI_CALLOC( 1, + sizeof( struct ldap_searchattr ))) == NULL ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_MEM ); + } + ( *sa )->sa_attrlabel = toks[ 0 ]; + ( *sa )->sa_attr = toks[ 1 ]; + ( *sa )->sa_selectattr = toks[ 3 ]; + ( *sa )->sa_selecttext = toks[ 4 ]; + /* Deal with bitmap */ + ( *sa )->sa_matchtypebitmap = 0; + for ( i = strlen( toks[ 2 ] ) - 1, j = 0; i >= 0; i--, j++ ) { + if ( toks[ 2 ][ i ] == '1' ) { + ( *sa )->sa_matchtypebitmap |= (1 << j); + } + } + NSLDAPI_FREE( toks[ 2 ] ); + NSLDAPI_FREE( ( char * ) toks ); + sa = &(( *sa )->sa_next); + } + *sa = NULL; + + /* + * Match types are last + */ + sm = &( so->so_smlist ); + while (( tokcnt = nsldapi_next_line_tokens( bufp, blenp, &toks )) > 0 ) { + if ( tokcnt < 2 ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_SYNTAX ); + } + if (( *sm = ( struct ldap_searchmatch * )NSLDAPI_CALLOC( 1, + sizeof( struct ldap_searchmatch ))) == NULL ) { + nsldapi_free_strarray( toks ); + ldap_free_searchprefs( so ); + return( LDAP_SEARCHPREF_ERR_MEM ); + } + ( *sm )->sm_matchprompt = toks[ 0 ]; + ( *sm )->sm_filter = toks[ 1 ]; + NSLDAPI_FREE( ( char * ) toks ); + sm = &(( *sm )->sm_next ); + } + *sm = NULL; + + *sop = so; + return( 0 ); +} diff --git a/ldap/c-sdk/libraries/libldap/test.c b/ldap/c-sdk/libraries/libldap/test.c new file mode 100644 index 0000000000..fa984f3430 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/test.c @@ -0,0 +1,1898 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* test.c - a simple test harness. */ +#include +#include +#include +#include +#ifdef MACOS +#ifdef THINK_C +#include +#include +#include +#endif /* THINK_C */ +#include "macos.h" +#else /* MACOS */ +#if defined( DOS ) +#include "msdos.h" +#if defined( WINSOCK ) +#include "console.h" +#endif /* WINSOCK */ +#else /* DOS */ +#ifdef _WINDOWS +#include +#include +#include +#include +//#include "console.h" +#else /* _WINDOWS */ +#include +#include +#include +#include +#include +#ifndef VMS +#include +#include +#endif /* VMS */ +#endif /* _WINDOWS */ +#endif /* DOS */ +#endif /* MACOS */ + +#include "ldap.h" +#include "disptmpl.h" +#include "ldaplog.h" +#include "portable.h" +#ifndef NO_LIBLCACHE +#include "lcache.h" +#endif /* !NO_LIBLCACHE */ + +#undef NET_SSL +#if defined(NET_SSL) +#include +#include +#endif + + +#if !defined( PCNFS ) && !defined( WINSOCK ) && !defined( MACOS ) +#define MOD_USE_BVALS +#endif /* !PCNFS && !WINSOCK && !MACOS */ + +static void handle_result( LDAP *ld, LDAPMessage *lm, int onlyone ); +static void print_ldap_result( LDAP *ld, LDAPMessage *lm, char *s ); +static void print_controls( LDAPControl **ctrls, int freeit ); +static void print_referrals( char **refs, int freeit ); +static void print_search_entry( LDAP *ld, LDAPMessage *res, int onlyone ); +static char *changetype_num2string( ber_int_t chgtype ); +static void print_search_reference( LDAP *ld, LDAPMessage *res, int onlyone ); +static void free_list( char **list ); +static int entry2textwrite( void *fp, char *buf, int len ); +static void bprint( char *data, int len ); +static char **string2words( char *str, char *delims ); +static const char * url_parse_err2string( int e ); + +char *dnsuffix; + +#ifndef WINSOCK +static char * +getline( char *line, int len, FILE *fp, char *prompt ) +{ + printf(prompt); + + if ( fgets( line, len, fp ) == NULL ) + return( NULL ); + + line[ strlen( line ) - 1 ] = '\0'; + + return( line ); +} +#endif /* WINSOCK */ + +static char ** +get_list( char *prompt ) +{ + static char buf[256]; + int num; + char **result; + + num = 0; + result = (char **) 0; + while ( 1 ) { + getline( buf, sizeof(buf), stdin, prompt ); + + if ( *buf == '\0' ) + break; + + if ( result == (char **) 0 ) + result = (char **) malloc( sizeof(char *) ); + else + result = (char **) realloc( result, + sizeof(char *) * (num + 1) ); + + result[num++] = (char *) strdup( buf ); + } + if ( result == (char **) 0 ) + return( NULL ); + result = (char **) realloc( result, sizeof(char *) * (num + 1) ); + result[num] = NULL; + + return( result ); +} + + +static void +free_list( char **list ) +{ + int i; + + if ( list != NULL ) { + for ( i = 0; list[ i ] != NULL; ++i ) { + free( list[ i ] ); + } + free( (char *)list ); + } +} + + +#ifdef MOD_USE_BVALS +static int +file_read( char *path, struct berval *bv ) +{ + FILE *fp; + long rlen; + int eof; + + if (( fp = NSLDAPI_FOPEN( path, "r" )) == NULL ) { + perror( path ); + return( -1 ); + } + + if ( fseek( fp, 0L, SEEK_END ) != 0 ) { + perror( path ); + fclose( fp ); + return( -1 ); + } + + bv->bv_len = ftell( fp ); + + if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) { + perror( "malloc" ); + fclose( fp ); + return( -1 ); + } + + if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { + perror( path ); + fclose( fp ); + return( -1 ); + } + + rlen = fread( bv->bv_val, 1, bv->bv_len, fp ); + eof = feof( fp ); + fclose( fp ); + + if ( (unsigned long)rlen != bv->bv_len ) { + perror( path ); + free( bv->bv_val ); + return( -1 ); + } + + return( bv->bv_len ); +} +#endif /* MOD_USE_BVALS */ + + +static LDAPMod ** +get_modlist( char *prompt1, char *prompt2, char *prompt3 ) +{ + static char buf[256]; + int num; + LDAPMod tmp; + LDAPMod **result; +#ifdef MOD_USE_BVALS + struct berval **bvals; +#endif /* MOD_USE_BVALS */ + + num = 0; + result = NULL; + while ( 1 ) { + if ( prompt1 ) { + getline( buf, sizeof(buf), stdin, prompt1 ); + tmp.mod_op = atoi( buf ); + + if ( tmp.mod_op == -1 || buf[0] == '\0' ) + break; + } else { + tmp.mod_op = 0; + } + + getline( buf, sizeof(buf), stdin, prompt2 ); + if ( buf[0] == '\0' ) + break; + tmp.mod_type = strdup( buf ); + + tmp.mod_values = get_list( prompt3 ); +#ifdef MOD_USE_BVALS + if ( tmp.mod_values != NULL ) { + int i; + + for ( i = 0; tmp.mod_values[i] != NULL; ++i ) + ; + bvals = (struct berval **)calloc( i + 1, + sizeof( struct berval *)); + for ( i = 0; tmp.mod_values[i] != NULL; ++i ) { + bvals[i] = (struct berval *)malloc( + sizeof( struct berval )); + if ( strncmp( tmp.mod_values[i], "{FILE}", + 6 ) == 0 ) { + if ( file_read( tmp.mod_values[i] + 6, + bvals[i] ) < 0 ) { + return( NULL ); + } + } else { + bvals[i]->bv_val = tmp.mod_values[i]; + bvals[i]->bv_len = + strlen( tmp.mod_values[i] ); + } + } + tmp.mod_bvalues = bvals; + tmp.mod_op |= LDAP_MOD_BVALUES; + } +#endif /* MOD_USE_BVALS */ + + if ( result == NULL ) + result = (LDAPMod **) malloc( sizeof(LDAPMod *) ); + else + result = (LDAPMod **) realloc( result, + sizeof(LDAPMod *) * (num + 1) ); + + result[num] = (LDAPMod *) malloc( sizeof(LDAPMod) ); + *(result[num]) = tmp; /* struct copy */ + num++; + } + if ( result == NULL ) + return( NULL ); + result = (LDAPMod **) realloc( result, sizeof(LDAPMod *) * (num + 1) ); + result[num] = NULL; + + return( result ); +} + + +int LDAP_CALL LDAP_CALLBACK +bind_prompt( LDAP *ld, char **dnp, char **passwdp, int *authmethodp, + int freeit, void *dummy ) +{ + static char dn[256], passwd[256]; + + if ( !freeit ) { +#ifdef KERBEROS + getline( dn, sizeof(dn), stdin, + "re-bind method (0->simple, 1->krbv41, 2->krbv42, 3->krbv41&2)? " ); + if (( *authmethodp = atoi( dn )) == 3 ) { + *authmethodp = LDAP_AUTH_KRBV4; + } else { + *authmethodp |= 0x80; + } +#else /* KERBEROS */ + *authmethodp = LDAP_AUTH_SIMPLE; +#endif /* KERBEROS */ + + getline( dn, sizeof(dn), stdin, "re-bind dn? " ); + strcat( dn, dnsuffix ); + *dnp = dn; + + if ( *authmethodp == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) { + getline( passwd, sizeof(passwd), stdin, + "re-bind password? " ); + } else { + passwd[0] = '\0'; + } + *passwdp = passwd; + } + + return( LDAP_SUCCESS ); +} + + +#define HEX2BIN( h ) ( (h) >= '0' && (h) <='9' ? (h) - '0' : (h) - 'A' + 10 ) + +void +berval_from_hex( struct berval *bvp, char *hexstr ) +{ + char *src, *dst, c; + unsigned char abyte; + + dst = bvp->bv_val; + bvp->bv_len = 0; + src = hexstr; + while ( *src != '\0' ) { + c = *src; + if ( isupper( c )) { + c = tolower( c ); + } + abyte = HEX2BIN( c ) << 4; + + ++src; + c = *src; + if ( isupper( c )) { + c = tolower( c ); + } + abyte |= HEX2BIN( c ); + ++src; + + *dst++ = abyte; + ++bvp->bv_len; + } +} + + +static void +add_control( LDAPControl ***ctrlsp, LDAPControl *newctrl ) +{ + int i; + + if ( *ctrlsp == NULL ) { + *ctrlsp = (LDAPControl **) calloc( 2, sizeof(LDAPControl *) ); + i = 0; + } else { + for ( i = 0; (*ctrlsp)[i] != NULL; i++ ) { + ; /* NULL */ + } + *ctrlsp = (LDAPControl **) realloc( *ctrlsp, + (i + 2) * sizeof(LDAPControl *) ); + } + (*ctrlsp)[i] = newctrl; + (*ctrlsp)[i+1] = NULL; +} + + +#ifdef TEST_CUSTOM_MALLOC + +typedef struct my_malloc_info { + long mmi_magic; + size_t mmi_actualsize; +} MyMallocInfo; +#define MY_MALLOC_MAGIC_NUMBER 0x19940618 + +#define MY_MALLOC_CHECK_MAGIC( p ) if ( ((MyMallocInfo *)( (p) - sizeof() + +void * +my_malloc( size_t size ) +{ + void *p; + MyMallocInfo *mmip; + + if (( p = malloc( size + sizeof( struct my_malloc_info ))) != NULL ) { + mmip = (MyMallocInfo *)p; + mmip->mmi_magic = MY_MALLOC_MAGIC_NUMBER; + mmip->mmi_actualsize = size; + } + + fprintf( stderr, "my_malloc: allocated ptr 0x%x, size %ld\n", + p, mmip->mmi_actualsize ); + + return( (char *)p + sizeof( MyMallocInfo )); +} + + +void * +my_calloc( size_t nelem, size_t elsize ) +{ + void *p; + + if (( p = my_malloc( nelem * elsize )) != NULL ) { + memset( p, 0, nelem * elsize ); + } + + return( p ); +} + + +void +my_free( void *ptr ) +{ + char *p; + MyMallocInfo *mmip; + + p = (char *)ptr; + p -= sizeof( MyMallocInfo ); + mmip = (MyMallocInfo *)p; + if ( mmip->mmi_magic != MY_MALLOC_MAGIC_NUMBER ) { + fprintf( stderr, + "my_malloc_check_magic: ptr 0x%x bad magic number\n", ptr ); + exit( 1 ); + } + + fprintf( stderr, "my_free: freeing ptr 0x%x, size %ld\n", + p, mmip->mmi_actualsize ); + + memset( p, 0, mmip->mmi_actualsize + sizeof( MyMallocInfo )); + free( p ); +} + + +void * +my_realloc( void *ptr, size_t size ) +{ + void *p; + MyMallocInfo *mmip; + + if ( ptr == NULL ) { + return( my_malloc( size )); + } + + mmip = (MyMallocInfo *)( (char *)ptr - sizeof( MyMallocInfo )); + if ( mmip->mmi_magic != MY_MALLOC_MAGIC_NUMBER ) { + fprintf( stderr, + "my_malloc_check_magic: ptr 0x%x bad magic number\n", ptr ); + exit( 1 ); + } + + if ( size <= mmip->mmi_actualsize ) { /* current block big enough? */ + return( ptr ); + } + + if (( p = my_malloc( size )) != NULL ) { + memcpy( p, ptr, mmip->mmi_actualsize ); + my_free( ptr ); + } + + return( p ); +} +#endif /* TEST_CUSTOM_MALLOC */ + +int +#ifdef WINSOCK +ldapmain( +#else /* WINSOCK */ +main( +#endif /* WINSOCK */ + int argc, char **argv ) +{ + LDAP *ld; + int rc, i, c, port, cldapflg, errflg, method, id, msgtype; + int version; + char line[256], command1, command2, command3; + char passwd[64], dn[256], rdn[64], attr[64], value[256]; + char filter[256], *host, **types; + char **exdn, *fnname; + int bound, all, scope, attrsonly, optval, ldapversion; + LDAPMessage *res; + LDAPMod **mods, **attrs; + struct timeval timeout, *tvp; + char *copyfname = NULL; + int copyoptions = 0; + LDAPURLDesc *ludp; + struct ldap_disptmpl *tmpllist = NULL; + int changetypes, changesonly, return_echg_ctls; + LDAPControl **tmpctrls, *newctrl, **controls = NULL; + char *usage = "usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file] [-V protocolversion]\n"; + + extern char *optarg; + extern int optind; + +#ifdef MACOS + if (( argv = get_list( "cmd line arg?" )) == NULL ) { + exit( 1 ); + } + for ( argc = 0; argv[ argc ] != NULL; ++argc ) { + ; + } +#endif /* MACOS */ + +#ifdef TEST_CUSTOM_MALLOC + { + struct ldap_memalloc_fns memalloc_fns; + + memalloc_fns.ldapmem_malloc = my_malloc; + memalloc_fns.ldapmem_calloc = my_calloc; + memalloc_fns.ldapmem_realloc = my_realloc; + memalloc_fns.ldapmem_free = my_free; + + if ( ldap_set_option( NULL, LDAP_OPT_MEMALLOC_FN_PTRS, + &memalloc_fns ) != 0 ) { + fputs( "ldap_set_option failed\n", stderr ); + exit( 1 ); + } + } +#endif /* TEST_CUSTOM_MALLOC */ + + host = NULL; + port = LDAP_PORT; + dnsuffix = ""; + cldapflg = errflg = 0; + ldapversion = 0; /* use default */ +#ifndef _WIN32 +#ifdef LDAP_DEBUG + ldap_debug = LDAP_DEBUG_ANY; +#endif +#endif + + while (( c = getopt( argc, argv, "uh:d:s:p:t:T:V:" )) != -1 ) { + switch( c ) { + case 'u': +#ifdef CLDAP + cldapflg++; +#else /* CLDAP */ + printf( "Compile with -DCLDAP for UDP support\n" ); +#endif /* CLDAP */ + break; + + case 'd': +#ifndef _WIN32 +#ifdef LDAP_DEBUG + ldap_debug = atoi( optarg ) | LDAP_DEBUG_ANY; + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, + &ldap_debug ); + } +#else + printf( "Compile with -DLDAP_DEBUG for debugging\n" ); +#endif +#endif + break; + + case 'h': + host = optarg; + break; + + case 's': + dnsuffix = optarg; + break; + + case 'p': + port = atoi( optarg ); + break; + +#if !defined(MACOS) && !defined(DOS) + case 't': /* copy ber's to given file */ + copyfname = strdup( optarg ); + copyoptions = LBER_SOCKBUF_OPT_TO_FILE; + break; + + case 'T': /* only output ber's to given file */ + copyfname = strdup( optarg ); + copyoptions = (LBER_SOCKBUF_OPT_TO_FILE | + LBER_SOCKBUF_OPT_TO_FILE_ONLY); + break; +#endif + case 'V': /* LDAP protocol version */ + ldapversion = atoi( optarg ); + break; + + default: + ++errflg; + } + } + + if ( host == NULL && optind == argc - 1 ) { + host = argv[ optind ]; + ++optind; + } + + if ( errflg || optind < argc - 1 ) { + fprintf( stderr, usage, argv[ 0 ] ); + exit( 1 ); + } + + printf( "%sldap_init( %s, %d )\n", cldapflg ? "c" : "", + host == NULL ? "(null)" : host, port ); + + if ( cldapflg ) { +#ifdef CLDAP + ld = cldap_open( host, port ); +#endif /* CLDAP */ + } else { + ld = ldap_init( host, port ); + } + + if ( ld == NULL ) { + perror( "ldap_init" ); + exit(1); + } + + if ( ldapversion != 0 && ldap_set_option( ld, + LDAP_OPT_PROTOCOL_VERSION, (void *)&ldapversion ) != 0 ) { + ldap_perror( ld, "ldap_set_option (protocol version)" ); + exit(1); + } + +#ifdef notdef +#if !defined(MACOS) && !defined(DOS) + if ( copyfname != NULL ) { + int fd; + Sockbuf *sb; + + if ( (fd = open( copyfname, O_WRONLY | O_CREAT, 0600 )) + == -1 ) { + perror( copyfname ); + exit ( 1 ); + } + ldap_get_option( ld, LDAP_OPT_SOCKBUF, &sb ); + ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_COPYDESC, + (void *) &fd ); + ber_sockbuf_set_option( sb, copyoptions, LBER_OPT_ON ); + } +#endif +#endif + + bound = 0; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + tvp = &timeout; + + (void) memset( line, '\0', sizeof(line) ); + while ( getline( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) { + command1 = line[0]; + command2 = line[1]; + command3 = line[2]; + + switch ( command1 ) { + case 'a': /* add or abandon */ + switch ( command2 ) { + case 'd': /* add */ + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (attrs = get_modlist( NULL, "attr? ", + "value? " )) == NULL ) + break; + if ( (id = ldap_add( ld, dn, attrs )) == -1 ) + ldap_perror( ld, "ldap_add" ); + else + printf( "Add initiated with id %d\n", + id ); + break; + + case 'b': /* abandon */ + getline( line, sizeof(line), stdin, "msgid? " ); + id = atoi( line ); + if ( ldap_abandon( ld, id ) != 0 ) + ldap_perror( ld, "ldap_abandon" ); + else + printf( "Abandon successful\n" ); + break; + default: + printf( "Possibilities: [ad]d, [ab]ort\n" ); + } + break; + + case 'v': /* ldap protocol version */ + getline( line, sizeof(line), stdin, + "ldap version? " ); + version = atoi( line ); + if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, + (void *) &version ) != 0 ) { + ldap_perror( ld, "ldap_set_option" ); + } + break; + + case 'b': /* asynch bind */ + getline( line, sizeof(line), stdin, + "method 0->simple 3->sasl? " ); + method = atoi( line ); + if ( method == 0 ) { + method = LDAP_AUTH_SIMPLE; + } else if ( method == 3 ) { + method = LDAP_AUTH_SASL; + } + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + + if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) { + } else { + passwd[0] = '\0'; + } + + if ( method == LDAP_AUTH_SIMPLE ) { + if ( dn[0] != '\0' ) { + getline( passwd, sizeof(passwd), stdin, + "password? " ); + } else { + passwd[0] = '\0'; + } + rc = ldap_simple_bind( ld, dn, passwd ); + } else { + struct berval cred; + char mechanism[BUFSIZ]; + + getline( mechanism, sizeof(mechanism), stdin, + "mechanism? " ); + getline( passwd, sizeof(passwd), stdin, + "credentials? " ); + cred.bv_val = passwd; + cred.bv_len = strlen( passwd ); + if ( ldap_sasl_bind( ld, dn, mechanism, &cred, + NULL, NULL, &rc ) != LDAP_SUCCESS ) { + rc = -1; + } + } + if ( rc == -1 ) { + fprintf( stderr, "ldap_bind failed\n" ); + ldap_perror( ld, "ldap_bind" ); + } else { + printf( "Bind initiated\n" ); + bound = 1; + } + break; + + case 'B': /* synch bind */ + getline( line, sizeof(line), stdin, + "method 0->simple 3->sasl? " ); + method = atoi( line ); + if ( method == 0 ) { + method = LDAP_AUTH_SIMPLE; + } else if ( method == 3 ) { + method = LDAP_AUTH_SASL; + } + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + + if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) { + } else { + passwd[0] = '\0'; + } + + if ( method == LDAP_AUTH_SIMPLE ) { + if ( dn[0] != '\0' ) { + getline( passwd, sizeof(passwd), stdin, + "password? " ); + } else { + passwd[0] = '\0'; + } + rc = ldap_simple_bind_s( ld, dn, passwd ); + fnname = "ldap_simple_bind_s"; + } else { + struct berval cred; + char mechanism[BUFSIZ]; + + getline( mechanism, sizeof(mechanism), stdin, + "mechanism? " ); + getline( passwd, sizeof(passwd), stdin, + "credentials? " ); + cred.bv_val = passwd; + cred.bv_len = strlen( passwd ); + rc = ldap_sasl_bind_s( ld, dn, mechanism, + &cred, NULL, NULL, NULL ); + fnname = "ldap_sasl_bind_s"; + } + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s failed\n", fnname ); + ldap_perror( ld, fnname ); + } else { + printf( "Bind successful\n" ); + bound = 1; + } + break; + + case 'c': /* compare */ + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + getline( attr, sizeof(attr), stdin, "attr? " ); + getline( value, sizeof(value), stdin, "value? " ); + + if ( (id = ldap_compare( ld, dn, attr, value )) == -1 ) + ldap_perror( ld, "ldap_compare" ); + else + printf( "Compare initiated with id %d\n", id ); + break; + + case 'x': /* extended operation */ + { + char oid[100]; + struct berval val; + + getline( oid, sizeof(oid), stdin, "oid? " ); + getline( value, sizeof(value), stdin, "value? " ); + if ( strncmp( value, "0x", 2 ) == 0 ) { + val.bv_val = (char *)malloc( strlen( value ) / 2 ); + berval_from_hex( &val, value + 2 ); + } else { + val.bv_val = strdup( value ); + val.bv_len = strlen( value ); + } + if ( ldap_extended_operation( ld, oid, &val, NULL, + NULL, &id ) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_extended_operation" ); + } else { + printf( "Extended op initiated with id %d\n", + id ); + } + free( val.bv_val ); + } + break; + + case 'C': /* set cache parameters */ +#ifdef NO_LIBLCACHE + getline( line, sizeof(line), stdin, + "cache init (memcache 0)? " ); +#else + getline( line, sizeof(line), stdin, + "cache init (memcache 0, lcache 1)? " ); +#endif + i = atoi( line ); + if ( i == 0 ) { /* memcache */ + unsigned long ttl, size; + char **basedns, *dnarray[2]; + LDAPMemCache *mc; + + getline( line, sizeof(line), stdin, + "memcache ttl? " ); + ttl = atoi( line ); + getline( line, sizeof(line), stdin, + "memcache size? " ); + size = atoi( line ); + getline( line, sizeof(line), stdin, + "memcache baseDN? " ); + if ( *line == '\0' ) { + basedns = NULL; + } else { + dnarray[0] = line; + dnarray[1] = NULL; + basedns = dnarray; + } + if (( rc = ldap_memcache_init( ttl, size, + basedns, NULL, &mc )) != LDAP_SUCCESS ) { + fprintf( stderr, + "ldap_memcache_init: %s\n", + ldap_err2string( rc )); + } else if (( rc = ldap_memcache_set( ld, mc )) + != LDAP_SUCCESS ) { + fprintf( stderr, + "ldap_memcache_set: %s\n", + ldap_err2string( rc )); + } + +#ifndef NO_LIBLCACHE + } else if ( i == 1 ) { + getline( line, sizeof(line), stdin, + "cache config file? " ); + if ( line[0] != '\0' ) { + if ( lcache_init( ld, line ) != 0 ) { + perror( "ldap_cache_init" ); + break; + } + } + getline( line, sizeof(line), stdin, + "cache on/off (on 1, off 0)? " ); + if ( line[0] != '\0' ) { + i = atoi( line ); + if ( ldap_set_option( ld, + LDAP_OPT_CACHE_ENABLE, &i ) != 0 ) { + ldap_perror( ld, "ldap_cache_enable" ); + break; + } + } + getline( line, sizeof(line), stdin, + "cache strategy (check 0, populate 1, localdb 2)? " ); + if ( line[0] != '\0' ) { + i = atoi( line ); + if ( ldap_set_option( ld, + LDAP_OPT_CACHE_STRATEGY, &i ) + != 0 ) { + ldap_perror(ld, "ldap_cache_strategy"); + break; + } + } +#endif /* !NO_LIBLCACHE */ + + } else { + fprintf( stderr, "unknown cachetype %d\n", i ); + } + break; + + case 'd': /* turn on debugging */ +#ifndef _WIN32 +#ifdef LDAP_DEBUG + getline( line, sizeof(line), stdin, "debug level? " ); + ldap_debug = atoi( line ) | LDAP_DEBUG_ANY; + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, + &ldap_debug ); + } +#else + printf( "Compile with -DLDAP_DEBUG for debugging\n" ); +#endif +#endif + break; + + case 'E': /* explode a dn */ + getline( line, sizeof(line), stdin, "dn? " ); + exdn = ldap_explode_dn( line, 0 ); + for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) { + printf( "\t\"%s\"\n", exdn[i] ); + } + break; + + case 'R': /* explode an rdn */ + getline( line, sizeof(line), stdin, "rdn? " ); + exdn = ldap_explode_rdn( line, 0 ); + for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) { + printf( "\t\"%s\"\n", exdn[i] ); + } + break; + + case 'm': /* modify or modifyrdn */ + if ( strncmp( line, "modify", 4 ) == 0 ) { + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (mods = get_modlist( + "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ", + "attribute type? ", "attribute value? " )) + == NULL ) + break; + if ( (id = ldap_modify( ld, dn, mods )) == -1 ) + ldap_perror( ld, "ldap_modify" ); + else + printf( "Modify initiated with id %d\n", + id ); + } else if ( strncmp( line, "modrdn", 4 ) == 0 ) { + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + getline( rdn, sizeof(rdn), stdin, "newrdn? " ); + getline( line, sizeof(line), stdin, + "deleteoldrdn? " ); + if ( (id = ldap_modrdn2( ld, dn, rdn, + atoi(line) )) == -1 ) + ldap_perror( ld, "ldap_modrdn" ); + else + printf( "Modrdn initiated with id %d\n", + id ); + } else { + printf( "Possibilities: [modi]fy, [modr]dn\n" ); + } + break; + + case 'q': /* quit */ +#ifdef CLDAP + if ( cldapflg ) + cldap_close( ld ); +#endif /* CLDAP */ + if ( !cldapflg ) + ldap_unbind( ld ); + exit( 0 ); + break; + + case 'r': /* result or remove */ + switch ( command3 ) { + case 's': /* result */ + getline( line, sizeof(line), stdin, + "msgid (-1=>any)? " ); + if ( line[0] == '\0' ) + id = -1; + else + id = atoi( line ); + getline( line, sizeof(line), stdin, + "all (0=>any, 1=>all)? " ); + if ( line[0] == '\0' ) + all = 1; + else + all = atoi( line ); + if (( msgtype = ldap_result( ld, id, all, + tvp, &res )) < 1 ) { + ldap_perror( ld, "ldap_result" ); + break; + } + printf( "\nresult: msgtype %d msgid %d\n", + msgtype, ldap_msgid( res ) ); + handle_result( ld, res, 0 ); + res = NULL; + break; + + case 'm': /* remove */ + getline( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (id = ldap_delete( ld, dn )) == -1 ) + ldap_perror( ld, "ldap_delete" ); + else + printf( "Remove initiated with id %d\n", + id ); + break; + + default: + printf( "Possibilities: [rem]ove, [res]ult\n" ); + break; + } + break; + + case 's': /* search */ + getline( dn, sizeof(dn), stdin, "searchbase? " ); + strcat( dn, dnsuffix ); + getline( line, sizeof(line), stdin, + "scope (0=Base, 1=One Level, 2=Subtree)? " ); + scope = atoi( line ); + getline( filter, sizeof(filter), stdin, + "search filter (e.g. sn=jones)? " ); + types = get_list( "attrs to return? " ); + getline( line, sizeof(line), stdin, + "attrsonly (0=attrs&values, 1=attrs only)? " ); + attrsonly = atoi( line ); + + if ( cldapflg ) { +#ifdef CLDAP + getline( line, sizeof(line), stdin, + "Requestor DN (for logging)? " ); + if ( cldap_search_s( ld, dn, scope, filter, types, + attrsonly, &res, line ) != 0 ) { + ldap_perror( ld, "cldap_search_s" ); + } else { + printf( "\nresult: msgid %d\n", + res->lm_msgid ); + handle_result( ld, res, 0 ); + res = NULL; + } +#endif /* CLDAP */ + } else { + if (( id = ldap_search( ld, dn, scope, filter, + types, attrsonly )) == -1 ) { + ldap_perror( ld, "ldap_search" ); + } else { + printf( "Search initiated with id %d\n", id ); + } + } + free_list( types ); + break; + + case 't': /* set timeout value */ + getline( line, sizeof(line), stdin, "timeout (-1=infinite)? " ); + timeout.tv_sec = atoi( line ); + if ( timeout.tv_sec < 0 ) { + tvp = NULL; + } else { + tvp = &timeout; + } + break; + + case 'U': /* set ufn search prefix */ + getline( line, sizeof(line), stdin, "ufn prefix? " ); + ldap_ufn_setprefix( ld, line ); + break; + + case 'u': /* user friendly search w/optional timeout */ + getline( dn, sizeof(dn), stdin, "ufn? " ); + strcat( dn, dnsuffix ); + types = get_list( "attrs to return? " ); + getline( line, sizeof(line), stdin, + "attrsonly (0=attrs&values, 1=attrs only)? " ); + attrsonly = atoi( line ); + + if ( command2 == 't' ) { + id = ldap_ufn_search_c( ld, dn, types, + attrsonly, &res, ldap_ufn_timeout, + &timeout ); + } else { + id = ldap_ufn_search_s( ld, dn, types, + attrsonly, &res ); + } + if ( res == NULL ) + ldap_perror( ld, "ldap_ufn_search" ); + else { + printf( "\nresult: err %d\n", id ); + handle_result( ld, res, 0 ); + res = NULL; + } + free_list( types ); + break; + + case 'l': /* URL search */ + getline( line, sizeof(line), stdin, + "attrsonly (0=attrs&values, 1=attrs only)? " ); + attrsonly = atoi( line ); + getline( line, sizeof(line), stdin, "LDAP URL? " ); + if (( id = ldap_url_search( ld, line, attrsonly )) + == -1 ) { + ldap_perror( ld, "ldap_url_search" ); + } else { + printf( "URL search initiated with id %d\n", id ); + } + break; + + case 'p': /* parse LDAP URL */ + getline( line, sizeof(line), stdin, "LDAP URL? " ); + if (( i = ldap_url_parse( line, &ludp )) != 0 ) { + fprintf( stderr, "ldap_url_parse: error %d (%s)\n", i, + url_parse_err2string( i )); + } else { + printf( "\t host: " ); + if ( ludp->lud_host == NULL ) { + printf( "DEFAULT\n" ); + } else { + printf( "<%s>\n", ludp->lud_host ); + } + printf( "\t port: " ); + if ( ludp->lud_port == 0 ) { + printf( "DEFAULT\n" ); + } else { + printf( "%d\n", ludp->lud_port ); + } + printf( "\tsecure: %s\n", ( ludp->lud_options & + LDAP_URL_OPT_SECURE ) != 0 ? "Yes" : "No" ); + printf( "\t dn: " ); + if ( ludp->lud_dn == NULL ) { + printf( "ROOT\n" ); + } else { + printf( "%s\n", ludp->lud_dn ); + } + printf( "\t attrs:" ); + if ( ludp->lud_attrs == NULL ) { + printf( " ALL" ); + } else { + for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) { + printf( " <%s>", ludp->lud_attrs[ i ] ); + } + } + printf( "\n\t scope: %s\n", ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? + "ONE" : ludp->lud_scope == LDAP_SCOPE_BASE ? "BASE" : + ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "SUB" : "**invalid**" ); + printf( "\tfilter: <%s>\n", ludp->lud_filter ); + ldap_free_urldesc( ludp ); + } + break; + + case 'n': /* set dn suffix, for convenience */ + getline( line, sizeof(line), stdin, "DN suffix? " ); + strcpy( dnsuffix, line ); + break; + + case 'N': /* add an LDAPv3 control */ + getline( line, sizeof(line), stdin, + "Control oid (. to clear list)? " ); + if ( *line == '.' && *(line+1) == '\0' ) { + controls = NULL; + } else { + newctrl = (LDAPControl *) malloc( + sizeof(LDAPControl) ); + newctrl->ldctl_oid = strdup( line ); + getline( line, sizeof(line), stdin, + "Control value? " ); + if ( strncmp( line, "0x", 2 ) == 0 ) { + newctrl->ldctl_value.bv_val = + (char *)malloc( strlen( line ) / 2 ); + berval_from_hex( &(newctrl->ldctl_value), + line + 2 ); + } else { + newctrl->ldctl_value.bv_val + = strdup( line ); + } + newctrl->ldctl_value.bv_len = strlen( line ); + getline( line, sizeof(line), stdin, + "Critical (0=no, 1=yes)? " ); + newctrl->ldctl_iscritical = atoi( line ); + add_control( &controls, newctrl ); + } + ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, + controls ); + ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, + &tmpctrls ); + print_controls( tmpctrls, 0 ); + break; + + case 'P': /* add a persistent search control */ + getline( line, sizeof(line), stdin, "Changetypes to " + " return (additive - add (1), delete (2), " + "modify (4), modDN (8))? " ); + changetypes = atoi(line); + getline( line, sizeof(line), stdin, + "Return changes only (0=no, 1=yes)? " ); + changesonly = atoi(line); + getline( line, sizeof(line), stdin, "Return entry " + "change controls (0=no, 1=yes)? " ); + return_echg_ctls = atoi(line); + getline( line, sizeof(line), stdin, + "Critical (0=no, 1=yes)? " ); + if ( ldap_create_persistentsearch_control( ld, + changetypes, changesonly, return_echg_ctls, + (char)atoi(line), &newctrl ) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_create_persistent" + "search_control" ); + } else { + add_control( &controls, newctrl ); + ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, + controls ); + ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, + &tmpctrls ); + print_controls( tmpctrls, 0 ); + } + break; + + case 'o': /* set ldap options */ + getline( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" ); + i = atoi( line ); + ldap_set_option( ld, LDAP_OPT_DEREF, &i ); + getline( line, sizeof(line), stdin, "timelimit?" ); + i = atoi( line ); + ldap_set_option( ld, LDAP_OPT_TIMELIMIT, &i ); + getline( line, sizeof(line), stdin, "sizelimit?" ); + i = atoi( line ); + ldap_set_option( ld, LDAP_OPT_SIZELIMIT, &i ); + +#ifdef STR_TRANSLATION + getline( line, sizeof(line), stdin, + "Automatic translation of T.61 strings (0=no, 1=yes)?" ); + if ( atoi( line ) == 0 ) { + ld->ld_lberoptions &= ~LBER_OPT_TRANSLATE_STRINGS; + } else { + ld->ld_lberoptions |= LBER_OPT_TRANSLATE_STRINGS; +#ifdef LDAP_CHARSET_8859 + getline( line, sizeof(line), stdin, + "Translate to/from ISO-8859 (0=no, 1=yes?" ); + if ( atoi( line ) != 0 ) { + ldap_set_string_translators( ld, + ldap_8859_to_t61, + ldap_t61_to_8859 ); + } +#endif /* LDAP_CHARSET_8859 */ + } +#endif /* STR_TRANSLATION */ + +#ifdef LDAP_DNS + getline( line, sizeof(line), stdin, + "Use DN & DNS to determine where to send requests (0=no, 1=yes)?" ); + optval = ( atoi( line ) != 0 ); + ldap_set_option( ld, LDAP_OPT_DNS, (void *) optval ); +#endif /* LDAP_DNS */ + + getline( line, sizeof(line), stdin, + "Recognize and chase referrals (0=no, 1=yes)?" ); + optval = ( atoi( line ) != 0 ); + ldap_set_option( ld, LDAP_OPT_REFERRALS, + (void *) optval ); + if ( optval ) { + getline( line, sizeof(line), stdin, + "Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" ); + if ( atoi( line ) != 0 ) { + ldap_set_rebind_proc( ld, bind_prompt, + NULL ); + } + } +#ifdef NET_SSL + getline( line, sizeof(line), stdin, + "Use Secure Sockets Layer - SSL (0=no, 1=yes)?" ); + optval = ( atoi( line ) != 0 ); + if ( optval ) { + getline( line, sizeof(line), stdin, + "security DB path?" ); + if ( ldapssl_client_init( (*line == '\0') ? + NULL : line, NULL ) < 0 ) { + perror( "ldapssl_client_init" ); + optval = 0; /* SSL not avail. */ + } else if ( ldapssl_install_routines( ld ) + < 0 ) { + ldap_perror( ld, + "ldapssl_install_routines" ); + optval = 0; /* SSL not avail. */ + } + } + + ldap_set_option( ld, LDAP_OPT_SSL, + optval ? LDAP_OPT_ON : LDAP_OPT_OFF ); + + getline( line, sizeof(line), stdin, + "Set SSL options (0=no, 1=yes)?" ); + optval = ( atoi( line ) != 0 ); + while ( 1 ) { + PRInt32 sslopt; + PRBool on; + + getline( line, sizeof(line), stdin, + "Option to set (0 if done)?" ); + sslopt = atoi(line); + if ( sslopt == 0 ) { + break; + } + getline( line, sizeof(line), stdin, + "On=1, Off=0?" ); + on = ( atoi( line ) != 0 ); + if ( ldapssl_set_option( ld, sslopt, on ) != 0 ) { + ldap_perror( ld, "ldapssl_set_option" ); + } + } +#endif + + getline( line, sizeof(line), stdin, "Reconnect?" ); + ldap_set_option( ld, LDAP_OPT_RECONNECT, + ( atoi( line ) == 0 ) ? LDAP_OPT_OFF : + LDAP_OPT_ON ); + + getline( line, sizeof(line), stdin, "Async I/O?" ); + ldap_set_option( ld, LDAP_OPT_ASYNC_CONNECT, + ( atoi( line ) == 0 ) ? LDAP_OPT_OFF : + LDAP_OPT_ON ); + break; + + case 'I': /* initialize display templates */ + getline( line, sizeof(line), stdin, + "Template file [ldaptemplates.conf]?" ); + if (( i = ldap_init_templates( *line == '\0' ? + "ldaptemplates.conf" : line, &tmpllist )) + != 0 ) { + fprintf( stderr, "ldap_init_templates: %s\n", + ldap_tmplerr2string( i )); + } + break; + + case 'T': /* read & display using template */ + getline( dn, sizeof(dn), stdin, "entry DN? " ); + strcat( dn, dnsuffix ); + if (( i = ldap_entry2text_search( ld, dn, NULL, NULL, + tmpllist, NULL, NULL, entry2textwrite, stdout, + "\n", 0, 0 )) != LDAP_SUCCESS ) { + fprintf( stderr, "ldap_entry2text_search: %s\n", + ldap_err2string( i )); + } + break; + + case 'L': /* set preferred language */ + getline( line, sizeof(line), stdin, + "Preferred language? " ); + if ( *line == '\0' ) { + ldap_set_option( ld, + LDAP_OPT_PREFERRED_LANGUAGE, NULL ); + } else { + ldap_set_option( ld, + LDAP_OPT_PREFERRED_LANGUAGE, line ); + } + break; + + case 'F': /* create filter */ + { + char filtbuf[ 512 ], pattern[ 512 ]; + char prefix[ 512 ], suffix[ 512 ]; + char attr[ 512 ], value[ 512 ]; + char *dupvalue, **words; + + getline( pattern, sizeof(pattern), stdin, + "pattern? " ); + getline( prefix, sizeof(prefix), stdin, + "prefix? " ); + getline( suffix, sizeof(suffix), stdin, + "suffix? " ); + getline( attr, sizeof(attr), stdin, + "attribute? " ); + getline( value, sizeof(value), stdin, + "value? " ); + + if (( dupvalue = strdup( value )) != NULL ) { + words = string2words( value, " " ); + } else { + words = NULL; + } + if ( ldap_create_filter( filtbuf, + sizeof(filtbuf), pattern, prefix, suffix, + attr, value, words) != 0 ) { + fprintf( stderr, + "ldap_create_filter failed\n" ); + } else { + printf( "filter is \"%s\"\n", filtbuf ); + } + if ( dupvalue != NULL ) free( dupvalue ); + if ( words != NULL ) free( words ); + } + break; + + case '?': /* help */ + case '\0': /* help */ + printf( "Commands: [ad]d [ab]andon [b]ind\n" ); + printf( " synch [B]ind [c]ompare [l]URL search\n" ); + printf( " [modi]fy [modr]dn [rem]ove\n" ); + printf( " [res]ult [s]earch [q]uit/unbind\n\n" ); + printf( " [u]fn search [ut]fn search with timeout\n" ); + printf( " [d]ebug [C]set cache parms[g]set msgid\n" ); + printf( " d[n]suffix [t]imeout [v]ersion\n" ); + printf( " [U]fn prefix [?]help [o]ptions\n" ); + printf( " [E]xplode dn [p]arse LDAP URL [R]explode RDN\n" ); + printf( " e[x]tended op [F]ilter create\n" ); + printf( " set co[N]trols set preferred [L]anguage\n" ); + printf( " add a [P]ersistent search control\n" ); + printf( " [I]nitialize display templates\n" ); + printf( " [T]read entry and display using template\n" ); + break; + + default: + printf( "Invalid command. Type ? for help.\n" ); + break; + } + + (void) memset( line, '\0', sizeof(line) ); + } + + return( 0 ); +} + +static void +handle_result( LDAP *ld, LDAPMessage *lm, int onlyone ) +{ + int msgtype; + + switch ( (msgtype = ldap_msgtype( lm )) ) { + case LDAP_RES_COMPARE: + printf( "Compare result\n" ); + print_ldap_result( ld, lm, "compare" ); + break; + + case LDAP_RES_SEARCH_RESULT: + printf( "Search result\n" ); + print_ldap_result( ld, lm, "search" ); + break; + + case LDAP_RES_SEARCH_ENTRY: + printf( "Search entry\n" ); + print_search_entry( ld, lm, onlyone ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + printf( "Search reference\n" ); + print_search_reference( ld, lm, onlyone ); + break; + + case LDAP_RES_ADD: + printf( "Add result\n" ); + print_ldap_result( ld, lm, "add" ); + break; + + case LDAP_RES_DELETE: + printf( "Delete result\n" ); + print_ldap_result( ld, lm, "delete" ); + break; + + case LDAP_RES_MODIFY: + printf( "Modify result\n" ); + print_ldap_result( ld, lm, "modify" ); + break; + + case LDAP_RES_MODRDN: + printf( "ModRDN result\n" ); + print_ldap_result( ld, lm, "modrdn" ); + break; + + case LDAP_RES_BIND: + printf( "Bind result\n" ); + print_ldap_result( ld, lm, "bind" ); + break; + case LDAP_RES_EXTENDED: + if ( ldap_msgid( lm ) == LDAP_RES_UNSOLICITED ) { + printf( "Unsolicited result\n" ); + print_ldap_result( ld, lm, "unsolicited" ); + } else { + printf( "ExtendedOp result\n" ); + print_ldap_result( ld, lm, "extendedop" ); + } + break; + + default: + printf( "Unknown result type 0x%x\n", msgtype ); + print_ldap_result( ld, lm, "unknown" ); + } + + if ( !onlyone ) { + ldap_msgfree( lm ); + } +} + +static void +print_ldap_result( LDAP *ld, LDAPMessage *lm, char *s ) +{ + int lderr; + char *matcheddn, *errmsg, *oid, **refs; + LDAPControl **ctrls; + struct berval *servercred, *data; + + if ( ldap_parse_result( ld, lm, &lderr, &matcheddn, &errmsg, &refs, + &ctrls, 0 ) != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_parse_result" ); + } else { + fprintf( stderr, "%s: %s", s, ldap_err2string( lderr )); + if ( lderr == LDAP_CONNECT_ERROR ) { + perror( " - " ); + } else { + fputc( '\n', stderr ); + } + if ( errmsg != NULL ) { + if ( *errmsg != '\0' ) { + fprintf( stderr, "Additional info: %s\n", + errmsg ); + } + ldap_memfree( errmsg ); + } + if ( matcheddn != NULL ) { + if ( NAME_ERROR( lderr )) { + fprintf( stderr, "Matched DN: %s\n", + matcheddn ); + } + ldap_memfree( matcheddn ); + } + print_referrals( refs, 1 ); + print_controls( ctrls, 1 ); + } + + /* if SASL bind response, get and show server credentials */ + if ( ldap_msgtype( lm ) == LDAP_RES_BIND && + ldap_parse_sasl_bind_result( ld, lm, &servercred, 0 ) == + LDAP_SUCCESS && servercred != NULL ) { + fputs( "\tSASL server credentials:\n", stderr ); + bprint( servercred->bv_val, servercred->bv_len ); + ber_bvfree( servercred ); + } + + /* if ExtendedOp response, get and show oid plus data */ + if ( ldap_msgtype( lm ) == LDAP_RES_EXTENDED && + ldap_parse_extended_result( ld, lm, &oid, &data, 0 ) == + LDAP_SUCCESS ) { + if ( oid != NULL ) { + if ( strcmp ( oid, LDAP_NOTICE_OF_DISCONNECTION ) + == 0 ) { + printf( + "\t%s Notice of Disconnection (OID: %s)\n", + s, oid ); + } else { + printf( "\t%s OID: %s\n", s, oid ); + } + ldap_memfree( oid ); + } + if ( data != NULL ) { + printf( "\t%s data:\n", s ); + bprint( data->bv_val, data->bv_len ); + ber_bvfree( data ); + } + } +} + +static void +print_search_entry( LDAP *ld, LDAPMessage *res, int onlyone ) +{ + BerElement *ber; + char *a, *dn, *ufn; + struct berval **vals; + int i, count; + LDAPMessage *e, *msg; + LDAPControl **ectrls; + + count = 0; + for ( msg = ldap_first_message( ld, res ); + msg != NULL && ( !onlyone || count == 0 ); + msg = ldap_next_message( ld, msg ), ++count ) { + if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) { + handle_result( ld, msg, 1 ); /* something else */ + continue; + } + e = msg; + + dn = ldap_get_dn( ld, e ); + printf( "\tDN: %s\n", dn ); + + ufn = ldap_dn2ufn( dn ); + printf( "\tUFN: %s\n", ufn ); +#ifdef WINSOCK + ldap_memfree( dn ); + ldap_memfree( ufn ); +#else /* WINSOCK */ + free( dn ); + free( ufn ); +#endif /* WINSOCK */ + + for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; + a = ldap_next_attribute( ld, e, ber ) ) { + printf( "\t\tATTR: %s\n", a ); + if ( (vals = ldap_get_values_len( ld, e, a )) + == NULL ) { + printf( "\t\t\t(no values)\n" ); + } else { + for ( i = 0; vals[i] != NULL; i++ ) { + int nonascii = 0; + unsigned long j; + + for ( j = 0; j < vals[i]->bv_len; j++ ) + if ( !isascii( vals[i]->bv_val[j] ) ) { + nonascii = 1; + break; + } + + if ( nonascii ) { + printf( "\t\t\tlength (%ld) (not ascii)\n", vals[i]->bv_len ); +#ifdef BPRINT_NONASCII + bprint( vals[i]->bv_val, + vals[i]->bv_len ); +#endif /* BPRINT_NONASCII */ + continue; + } + printf( "\t\t\tlength (%ld) %s\n", + vals[i]->bv_len, vals[i]->bv_val ); + } + ber_bvecfree( vals ); + } + ldap_memfree( a ); + } + if ( ldap_get_lderrno( ld, NULL, NULL ) != LDAP_SUCCESS ) { + ldap_perror( ld, + "ldap_first_attribute/ldap_next_attribute" ); + } + if ( ber != NULL ) { + ber_free( ber, 0 ); + } + + if ( ldap_get_entry_controls( ld, e, &ectrls ) + != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_get_entry_controls" ); + } else { + int changenumpresent; + ber_int_t changetype; + char *prevdn; + ber_int_t changenum; + + if ( ldap_parse_entrychange_control( ld, ectrls, + &changetype, &prevdn, &changenumpresent, + &changenum ) == LDAP_SUCCESS ) { + fprintf( stderr, "EntryChangeNotification\n" + "\tchangeType: %s\n", + changetype_num2string( changetype )); + if ( prevdn != NULL ) { + fprintf( stderr, + "\tpreviousDN: \"%s\"\n", + prevdn ); + } + if ( changenumpresent ) { + fprintf( stderr, "\tchangeNumber: %d\n", + changenum ); + } + if ( prevdn != NULL ) { + free( prevdn ); + } + } + print_controls( ectrls, 1 ); + } + } +} + + +static char * +changetype_num2string( ber_int_t chgtype ) +{ + static char buf[ 25 ]; + char *s; + + switch( chgtype ) { + case LDAP_CHANGETYPE_ADD: + s = "add"; + break; + case LDAP_CHANGETYPE_DELETE: + s = "delete"; + break; + case LDAP_CHANGETYPE_MODIFY: + s = "modify"; + break; + case LDAP_CHANGETYPE_MODDN: + s = "moddn"; + break; + default: + s = buf; + sprintf( s, "unknown (%d)", chgtype ); + } + + return( s ); +} + + +static void +print_search_reference( LDAP *ld, LDAPMessage *res, int onlyone ) +{ + LDAPMessage *msg; + LDAPControl **ctrls; + char **refs; + int count; + + count = 0; + for ( msg = ldap_first_message( ld, res ); + msg != NULL && ( !onlyone || count == 0 ); + msg = ldap_next_message( ld, msg ), ++count ) { + if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_REFERENCE ) { + handle_result( ld, msg, 1 ); /* something else */ + continue; + } + + if ( ldap_parse_reference( ld, msg, &refs, &ctrls, 0 ) != + LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_parse_reference" ); + } else { + print_referrals( refs, 1 ); + print_controls( ctrls, 1 ); + } + } +} + + +static void +print_referrals( char **refs, int freeit ) +{ + int i; + + if ( refs == NULL ) { + return; + } + + fprintf( stderr, "Referrals:\n" ); + for ( i = 0; refs[ i ] != NULL; ++i ) { + fprintf( stderr, "\t%s\n", refs[ i ] ); + } + + if ( freeit ) { + ldap_value_free( refs ); + } +} + + +static void +print_controls( LDAPControl **ctrls, int freeit ) +{ + int i; + + if ( ctrls == NULL ) { + return; + } + + fprintf( stderr, "Controls:\n" ); + for ( i = 0; ctrls[ i ] != NULL; ++i ) { + if ( i > 0 ) { + fputs( "\t-----------\n", stderr ); + } + fprintf( stderr, "\toid: %s\n", ctrls[ i ]->ldctl_oid ); + fprintf( stderr, "\tcritical: %s\n", + ctrls[ i ]->ldctl_iscritical ? "YES" : "NO" ); + fputs( "\tvalue:\n", stderr ); + bprint( ctrls[ i ]->ldctl_value.bv_val, + ctrls[ i ]->ldctl_value.bv_len ); + } + + if ( freeit ) { + ldap_controls_free( ctrls ); + } +} + + +static int +entry2textwrite( void *fp, char *buf, int len ) +{ + return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len ); +} + + +/* similar to getfilter.c:break_into_words() */ +static char ** +string2words( char *str, char *delims ) +{ + char *word, **words; + int count; + char *lasts; + + if (( words = (char **)calloc( 1, sizeof( char * ))) == NULL ) { + return( NULL ); + } + count = 0; + words[ count ] = NULL; + + word = ldap_utf8strtok_r( str, delims, &lasts ); + while ( word != NULL ) { + if (( words = (char **)realloc( words, + ( count + 2 ) * sizeof( char * ))) == NULL ) { + free( words ); + return( NULL ); + } + + words[ count ] = word; + words[ ++count ] = NULL; + word = ldap_utf8strtok_r( NULL, delims, &lasts ); + } + + return( words ); +} + + +static const char * +url_parse_err2string( int e ) +{ + const char *s = "unknown"; + + switch( e ) { + case LDAP_URL_ERR_NOTLDAP: + s = "URL doesn't begin with \"ldap://\""; + break; + case LDAP_URL_ERR_NODN: + s = "URL has no DN (required)"; + break; + case LDAP_URL_ERR_BADSCOPE: + s = "URL scope string is invalid"; + break; + case LDAP_URL_ERR_MEM: + s = "can't allocate memory space"; + break; + case LDAP_URL_ERR_PARAM: + s = "bad parameter to an URL function"; + break; + case LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION: + s = "unrecognized critical URL extension"; + break; + } + + return( s ); +} + + +/* + * Print arbitrary stuff, for debugging. + */ + +#define BPLEN 48 +static void +bprint( char *data, int len ) +{ + static char hexdig[] = "0123456789abcdef"; + char out[ BPLEN ]; + int i = 0; + + memset( out, 0, BPLEN ); + for ( ;; ) { + if ( len < 1 ) { + fprintf( stderr, "\t%s\n", ( i == 0 ) ? "(end)" : out ); + break; + } + +#ifndef HEX + if ( isgraph( (unsigned char)*data )) { + out[ i ] = ' '; + out[ i+1 ] = *data; + } else { +#endif + out[ i ] = hexdig[ ( *data & 0xf0 ) >> 4 ]; + out[ i+1 ] = hexdig[ *data & 0x0f ]; +#ifndef HEX + } +#endif + i += 2; + len--; + data++; + + if ( i > BPLEN - 2 ) { + fprintf( stderr, "\t%s\n", out ); + memset( out, 0, BPLEN ); + i = 0; + continue; + } + out[ i++ ] = ' '; + } + + fflush( stderr ); +} diff --git a/ldap/c-sdk/libraries/libldap/tmplout.c b/ldap/c-sdk/libraries/libldap/tmplout.c new file mode 100644 index 0000000000..0dded6b4c5 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/tmplout.c @@ -0,0 +1,1144 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * tmplout.c: display template library output routines for LDAP clients + * + */ + +#include "ldap-int.h" +#include "disptmpl.h" + +#if defined(_WINDOWS) || defined(aix) || defined(SCOOS) || defined(OSF1) || defined(SOLARIS) +#include /* for struct tm and ctime */ +#endif + + +/* This is totally lame, since it should be coming from time.h, but isn't. */ +#if defined(SOLARIS) +char *ctime_r(const time_t *, char *, int); +#endif + +static int do_entry2text( LDAP *ld, char *buf, char *base, LDAPMessage *entry, + struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals, + writeptype writeproc, void *writeparm, char *eol, int rdncount, + unsigned long opts, char *urlprefix ); +static int do_entry2text_search( LDAP *ld, char *dn, char *base, + LDAPMessage *entry, struct ldap_disptmpl *tmpllist, char **defattrs, + char ***defvals, writeptype writeproc, void *writeparm, char *eol, + int rdncount, unsigned long opts, char *urlprefix ); +static int do_vals2text( LDAP *ld, char *buf, char **vals, char *label, + int labelwidth, unsigned long syntaxid, writeptype writeproc, + void *writeparm, char *eol, int rdncount, char *urlprefix ); +static int max_label_len( struct ldap_disptmpl *tmpl ); +static int output_label( char *buf, char *label, int width, + writeptype writeproc, void *writeparm, char *eol, int html ); +static int output_dn( char *buf, char *dn, int width, int rdncount, + writeptype writeproc, void *writeparm, char *eol, char *urlprefix ); +static void strcat_escaped( char *s1, char *s2 ); +static char *time2text( char *ldtimestr, int dateonly ); +static long gtime( struct tm *tm ); +static int searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, + char *dn, struct ldap_tmplitem *tip, int labelwidth, int rdncount, + writeptype writeproc, void *writeparm, char *eol, char *urlprefix ); + +#define DEF_LABEL_WIDTH 15 +#define SEARCH_TIMEOUT_SECS 120 +#define OCATTRNAME "objectClass" + + +#define NONFATAL_LDAP_ERR( err ) ( err == LDAP_SUCCESS || \ + err == LDAP_TIMELIMIT_EXCEEDED || err == LDAP_SIZELIMIT_EXCEEDED ) + +#define DEF_LDAP_URL_PREFIX "ldap:///" + + +int +LDAP_CALL +ldap_entry2text( + LDAP *ld, + char *buf, /* NULL for "use internal" */ + LDAPMessage *entry, + struct ldap_disptmpl *tmpl, + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, + unsigned long opts +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2text\n", 0, 0, 0 ); + + return( do_entry2text( ld, buf, NULL, entry, tmpl, defattrs, defvals, + writeproc, writeparm, eol, rdncount, opts, NULL )); + +} + + + +int +LDAP_CALL +ldap_entry2html( + LDAP *ld, + char *buf, /* NULL for "use internal" */ + LDAPMessage *entry, + struct ldap_disptmpl *tmpl, + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, + unsigned long opts, + char *base, + char *urlprefix +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2html\n", 0, 0, 0 ); + + if ( urlprefix == NULL ) { + urlprefix = DEF_LDAP_URL_PREFIX; + } + + return( do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals, + writeproc, writeparm, eol, rdncount, opts, urlprefix )); +} + + +static int +do_entry2text( + LDAP *ld, + char *buf, /* NULL for use-internal */ + char *base, /* used for search actions */ + LDAPMessage *entry, + struct ldap_disptmpl *tmpl, + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, + unsigned long opts, + char *urlprefix /* if non-NULL, do HTML */ +) +{ + int i, err, html, show, labelwidth; + int freebuf, freevals; + char *dn, **vals; + struct ldap_tmplitem *rowp, *colp; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( writeproc == NULL || + !NSLDAPI_VALID_LDAPMESSAGE_ENTRY_POINTER( entry )) { + err = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + if (( dn = ldap_get_dn( ld, entry )) == NULL ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( buf == NULL ) { + if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + NSLDAPI_FREE( dn ); + return( err ); + } + freebuf = 1; + } else { + freebuf = 0; + } + + html = ( urlprefix != NULL ); + + if ( html ) { + /* + * add HTML intro. and title + */ + if (!(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) { + sprintf( buf, "%s%s%s%s - ", eol, eol, eol, + ( tmpl == NULL ) ? "Entry" : tmpl->dt_name ); + (*writeproc)( writeparm, buf, strlen( buf )); + output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); + sprintf( buf, "%s%s%s%s

%s - ", eol, eol, + eol, eol, ( tmpl == NULL ) ? "Entry" : tmpl->dt_name ); + (*writeproc)( writeparm, buf, strlen( buf )); + output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); + sprintf( buf, "

%s", eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + } + + if (( opts & LDAP_DISP_OPT_NONLEAF ) != 0 && + ( vals = ldap_explode_dn( dn, 0 )) != NULL ) { + char *untagged; + + /* + * add "Move Up" link + */ + sprintf( buf, "
1 ) { + strcat_escaped( buf, ", " ); + } + strcat_escaped( buf, vals[ i ] ); + } + if ( vals[ 1 ] != NULL ) { + untagged = strchr( vals[ 1 ], '=' ); + } else { + untagged = "=The World"; + } + sprintf( buf + strlen( buf ), + "%s\">Move Up To %s%s
", + ( vals[ 1 ] == NULL ) ? "??one" : "", + ( untagged != NULL ) ? untagged + 1 : vals[ 1 ], eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + + /* + * add "Browse" link + */ + untagged = strchr( vals[ 0 ], '=' ); + sprintf( buf, "Browse Below %s%s%s", + ( untagged != NULL ) ? untagged + 1 : vals[ 0 ], eol, eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + + ldap_value_free( vals ); + } + + (*writeproc)( writeparm, "
", 4 ); /* horizontal rule */ + } else { + (*writeproc)( writeparm, "\"", 1 ); + output_dn( buf, dn, 0, rdncount, writeproc, writeparm, "", NULL ); + sprintf( buf, "\"%s", eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + } + + if ( tmpl != NULL && ( opts & LDAP_DISP_OPT_AUTOLABELWIDTH ) != 0 ) { + labelwidth = max_label_len( tmpl ) + 3; + } else { + labelwidth = DEF_LABEL_WIDTH;; + } + + err = LDAP_SUCCESS; + + if ( tmpl == NULL ) { + BerElement *ber; + char *attr; + + ber = NULL; + for ( attr = ldap_first_attribute( ld, entry, &ber ); + NONFATAL_LDAP_ERR( err ) && attr != NULL; + attr = ldap_next_attribute( ld, entry, ber )) { + if (( vals = ldap_get_values( ld, entry, attr )) == NULL ) { + freevals = 0; + if ( defattrs != NULL ) { + for ( i = 0; defattrs[ i ] != NULL; ++i ) { + if ( strcasecmp( attr, defattrs[ i ] ) == 0 ) { + break; + } + } + if ( defattrs[ i ] != NULL ) { + vals = defvals[ i ]; + } + } + } else { + freevals = 1; + } + + if ( islower( *attr )) { /* cosmetic -- upcase attr. name */ + *attr = toupper( *attr ); + } + + err = do_vals2text( ld, buf, vals, attr, labelwidth, + LDAP_SYN_CASEIGNORESTR, writeproc, writeparm, eol, + rdncount, urlprefix ); + if ( freevals ) { + ldap_value_free( vals ); + } + } + if ( ber == NULL ) { + ber_free( ber, 0 ); + } + /* + * XXX check for errors in ldap_first_attribute/ldap_next_attribute + * here (but what should we do if there was one?) + */ + + } else { + for ( rowp = ldap_first_tmplrow( tmpl ); + NONFATAL_LDAP_ERR( err ) && rowp != NULLTMPLITEM; + rowp = ldap_next_tmplrow( tmpl, rowp )) { + for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM; + colp = ldap_next_tmplcol( tmpl, rowp, colp )) { + vals = NULL; + if ( colp->ti_attrname == NULL || ( vals = ldap_get_values( ld, + entry, colp->ti_attrname )) == NULL ) { + freevals = 0; + if ( !LDAP_IS_TMPLITEM_OPTION_SET( colp, + LDAP_DITEM_OPT_HIDEIFEMPTY ) && defattrs != NULL + && colp->ti_attrname != NULL ) { + for ( i = 0; defattrs[ i ] != NULL; ++i ) { + if ( strcasecmp( colp->ti_attrname, defattrs[ i ] ) + == 0 ) { + break; + } + } + if ( defattrs[ i ] != NULL ) { + vals = defvals[ i ]; + } + } + } else { + freevals = 1; + if ( LDAP_IS_TMPLITEM_OPTION_SET( colp, + LDAP_DITEM_OPT_SORTVALUES ) && vals[ 0 ] != NULL + && vals[ 1 ] != NULL ) { + ldap_sort_values(ld, vals, ldap_sort_strcasecmp); + } + } + + /* + * don't bother even calling do_vals2text() if no values + * or boolean with value false and "hide if false" option set + */ + show = ( vals != NULL && vals[ 0 ] != NULL ); + if ( show && LDAP_GET_SYN_TYPE( colp->ti_syntaxid ) + == LDAP_SYN_TYPE_BOOLEAN && LDAP_IS_TMPLITEM_OPTION_SET( + colp, LDAP_DITEM_OPT_HIDEIFFALSE ) && + toupper( vals[ 0 ][ 0 ] ) != 'T' ) { + show = 0; + } + + if ( colp->ti_syntaxid == LDAP_SYN_SEARCHACTION ) { + if (( opts & LDAP_DISP_OPT_DOSEARCHACTIONS ) != 0 ) { + if ( colp->ti_attrname == NULL || ( show && + toupper( vals[ 0 ][ 0 ] ) == 'T' )) { + err = searchaction( ld, buf, base, entry, dn, colp, + labelwidth, rdncount, writeproc, + writeparm, eol, urlprefix ); + } + } + show = 0; + } + + if ( show ) { + err = do_vals2text( ld, buf, vals, colp->ti_label, + labelwidth, colp->ti_syntaxid, writeproc, writeparm, + eol, rdncount, urlprefix ); + } + + if ( freevals ) { + ldap_value_free( vals ); + } + } + } + } + + if ( html && !(( opts & LDAP_DISP_OPT_HTMLBODYONLY ) != 0 )) { + sprintf( buf, "%s%s", eol, eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + } + + NSLDAPI_FREE( dn ); + if ( freebuf ) { + NSLDAPI_FREE( buf ); + } + + return( err ); +} + + +int +LDAP_CALL +ldap_entry2text_search( + LDAP *ld, + char *dn, /* if NULL, use entry */ + char *base, /* if NULL, no search actions */ + LDAPMessage *entry, /* if NULL, use dn */ + struct ldap_disptmpl* tmpllist, /* if NULL, load default file */ + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, /* if 0, display full DN */ + unsigned long opts +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2text_search\n", 0, 0, 0 ); + + return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs, + defvals, writeproc, writeparm, eol, rdncount, opts, NULL )); +} + + + +int +LDAP_CALL +ldap_entry2html_search( + LDAP *ld, + char *dn, /* if NULL, use entry */ + char *base, /* if NULL, no search actions */ + LDAPMessage *entry, /* if NULL, use dn */ + struct ldap_disptmpl* tmpllist, /* if NULL, load default file */ + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, /* if 0, display full DN */ + unsigned long opts, + char *urlprefix +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_entry2html_search\n", 0, 0, 0 ); + + return( do_entry2text_search( ld, dn, base, entry, tmpllist, defattrs, + defvals, writeproc, writeparm, eol, rdncount, opts, urlprefix )); +} + + +static int +do_entry2text_search( + LDAP *ld, + char *dn, /* if NULL, use entry */ + char *base, /* if NULL, no search actions */ + LDAPMessage *entry, /* if NULL, use dn */ + struct ldap_disptmpl* tmpllist, /* if NULL, no template used */ + char **defattrs, + char ***defvals, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, /* if 0, display full DN */ + unsigned long opts, + char *urlprefix +) +{ + int err, freedn, html; + char *buf, **fetchattrs, **vals; + LDAPMessage *ldmp; + struct ldap_disptmpl *tmpl; + struct timeval timeout; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( dn == NULL && entry == NULLMSG ) { + err = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + html = ( urlprefix != NULL ); + + timeout.tv_sec = SEARCH_TIMEOUT_SECS; + timeout.tv_usec = 0; + + if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + freedn = 0; + tmpl = NULL; + + if ( dn == NULL ) { + if (( dn = ldap_get_dn( ld, entry )) == NULL ) { + NSLDAPI_FREE( buf ); + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + freedn = 1; + } + + + if ( tmpllist != NULL ) { + ldmp = NULLMSG; + + if ( entry == NULL ) { + char *ocattrs[2]; + + ocattrs[0] = OCATTRNAME; + ocattrs[1] = NULL; +#ifdef CLDAP + if ( LDAP_IS_CLDAP( ld )) + err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, + "objectClass=*", ocattrs, 0, &ldmp, NULL ); + else +#endif /* CLDAP */ + err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, + "objectClass=*", ocattrs, 0, &timeout, &ldmp ); + + if ( err == LDAP_SUCCESS ) { + entry = ldap_first_entry( ld, ldmp ); + } + } + + if ( entry != NULL ) { + vals = ldap_get_values( ld, entry, OCATTRNAME ); + tmpl = ldap_oc2template( vals, tmpllist ); + if ( vals != NULL ) { + ldap_value_free( vals ); + } + } + if ( ldmp != NULL ) { + ldap_msgfree( ldmp ); + } + } + + entry = NULL; + + if ( tmpl == NULL ) { + fetchattrs = NULL; + } else { + fetchattrs = ldap_tmplattrs( tmpl, NULL, 1, LDAP_SYN_OPT_DEFER ); + } + +#ifdef CLDAP + if ( LDAP_IS_CLDAP( ld )) + err = cldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", + fetchattrs, 0, &ldmp, NULL ); + else +#endif /* CLDAP */ + err = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", + fetchattrs, 0, &timeout, &ldmp ); + + if ( freedn ) { + NSLDAPI_FREE( dn ); + } + if ( fetchattrs != NULL ) { + ldap_value_free( fetchattrs ); + } + + if ( err != LDAP_SUCCESS || + ( entry = ldap_first_entry( ld, ldmp )) == NULL ) { + NSLDAPI_FREE( buf ); + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + err = do_entry2text( ld, buf, base, entry, tmpl, defattrs, defvals, + writeproc, writeparm, eol, rdncount, opts, urlprefix ); + + NSLDAPI_FREE( buf ); + ldap_msgfree( ldmp ); + return( err ); +} + + +int +LDAP_CALL +ldap_vals2text( + LDAP *ld, + char *buf, /* NULL for "use internal" */ + char **vals, + char *label, + int labelwidth, /* 0 means use default */ + unsigned long syntaxid, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_vals2text\n", 0, 0, 0 ); + + return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid, + writeproc, writeparm, eol, rdncount, NULL )); +} + + +int +LDAP_CALL +ldap_vals2html( + LDAP *ld, + char *buf, /* NULL for "use internal" */ + char **vals, + char *label, + int labelwidth, /* 0 means use default */ + unsigned long syntaxid, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, + char *urlprefix +) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_vals2html\n", 0, 0, 0 ); + + if ( urlprefix == NULL ) { + urlprefix = DEF_LDAP_URL_PREFIX; + } + + return( do_vals2text( ld, buf, vals, label, labelwidth, syntaxid, + writeproc, writeparm, eol, rdncount, urlprefix )); +} + + +static int +do_vals2text( + LDAP *ld, + char *buf, /* NULL for "use internal" */ + char **vals, + char *label, + int labelwidth, /* 0 means use default */ + unsigned long syntaxid, + writeptype writeproc, + void *writeparm, + char *eol, + int rdncount, + char *urlprefix +) +{ + int err, i, html, writeoutval, freebuf, notascii; + char *p, *s, *outval; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || writeproc == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + if ( vals == NULL ) { + return( LDAP_SUCCESS ); + } + + html = ( urlprefix != NULL ); + + switch( LDAP_GET_SYN_TYPE( syntaxid )) { + case LDAP_SYN_TYPE_TEXT: + case LDAP_SYN_TYPE_BOOLEAN: + break; /* we only bother with these two types... */ + default: + return( LDAP_SUCCESS ); + } + + if ( labelwidth == 0 || labelwidth < 0 ) { + labelwidth = DEF_LABEL_WIDTH; + } + + if ( buf == NULL ) { + if (( buf = NSLDAPI_MALLOC( LDAP_DTMPL_BUFSIZ )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + freebuf = 1; + } else { + freebuf = 0; + } + + output_label( buf, label, labelwidth, writeproc, writeparm, eol, html ); + + for ( i = 0; vals[ i ] != NULL; ++i ) { + for ( p = vals[ i ]; *p != '\0'; ++p ) { + if ( !isascii( *p )) { + break; + } + } + notascii = ( *p != '\0' ); + outval = notascii ? "(unable to display non-ASCII text value)" + : vals[ i ]; + + writeoutval = 0; /* if non-zero, write outval after switch */ + + switch( syntaxid ) { + case LDAP_SYN_CASEIGNORESTR: + ++writeoutval; + break; + + case LDAP_SYN_RFC822ADDR: + if ( html ) { + strcpy( buf, "
%s
%s", outval, eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + } else { + ++writeoutval; + } + break; + + case LDAP_SYN_DN: /* for now */ + output_dn( buf, outval, labelwidth, rdncount, writeproc, + writeparm, eol, urlprefix ); + break; + + case LDAP_SYN_MULTILINESTR: + if ( i > 0 && !html ) { + output_label( buf, label, labelwidth, writeproc, + writeparm, eol, html ); + } + + p = s = outval; + while (( s = strchr( s, '$' )) != NULL ) { + *s++ = '\0'; + while ( ldap_utf8isspace( s )) { + ++s; + } + if ( html ) { + sprintf( buf, "
%s
%s", p, eol ); + } else { + sprintf( buf, "%-*s%s%s", labelwidth, " ", p, eol ); + } + (*writeproc)( writeparm, buf, strlen( buf )); + p = s; + } + outval = p; + ++writeoutval; + break; + + case LDAP_SYN_BOOLEAN: + outval = toupper( outval[ 0 ] ) == 'T' ? "TRUE" : "FALSE"; + ++writeoutval; + break; + + case LDAP_SYN_TIME: + case LDAP_SYN_DATE: + outval = time2text( outval, syntaxid == LDAP_SYN_DATE ); + ++writeoutval; + break; + + case LDAP_SYN_LABELEDURL: + if ( !notascii && ( p = strchr( outval, '$' )) != NULL ) { + *p++ = '\0'; + while ( ldap_utf8isspace( p )) { + ++p; + } + s = outval; + } else if ( !notascii && ( s = strchr( outval, ' ' )) != NULL ) { + *s++ = '\0'; + while ( ldap_utf8isspace( s )) { + ++s; + } + p = outval; + } else { + s = "URL"; + p = outval; + } + + /* + * at this point `s' points to the label & `p' to the URL + */ + if ( html ) { + sprintf( buf, "
%s
%s", p, s, eol ); + } else { + sprintf( buf, "%-*s%s%s%-*s%s%s", labelwidth, " ", + s, eol, labelwidth + 2, " ",p , eol ); + } + (*writeproc)( writeparm, buf, strlen( buf )); + break; + + default: + sprintf( buf, " Can't display item type %ld%s", + syntaxid, eol ); + (*writeproc)( writeparm, buf, strlen( buf )); + } + + if ( writeoutval ) { + if ( html ) { + sprintf( buf, "
%s
%s", outval, eol ); + } else { + sprintf( buf, "%-*s%s%s", labelwidth, " ", outval, eol ); + } + (*writeproc)( writeparm, buf, strlen( buf )); + } + } + + if ( freebuf ) { + NSLDAPI_FREE( buf ); + } + + return( LDAP_SUCCESS ); +} + + +static int +max_label_len( struct ldap_disptmpl *tmpl ) +{ + struct ldap_tmplitem *rowp, *colp; + int len, maxlen; + + maxlen = 0; + + for ( rowp = ldap_first_tmplrow( tmpl ); rowp != NULLTMPLITEM; + rowp = ldap_next_tmplrow( tmpl, rowp )) { + for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM; + colp = ldap_next_tmplcol( tmpl, rowp, colp )) { + if (( len = strlen( colp->ti_label )) > maxlen ) { + maxlen = len; + } + } + } + + return( maxlen ); +} + + +static int +output_label( char *buf, char *label, int width, writeptype writeproc, + void *writeparm, char *eol, int html ) +{ + char *p; + + if ( html ) { + sprintf( buf, "
%s", label ); + } else { + auto size_t w; + sprintf( buf, " %s:", label ); + p = buf + strlen( buf ); + + for (w = ldap_utf8characters(buf); w < (size_t)width; ++w) { + *p++ = ' '; + } + + *p = '\0'; + strcat( buf, eol ); + } + + return ((*writeproc)( writeparm, buf, strlen( buf ))); +} + + +static int +output_dn( char *buf, char *dn, int width, int rdncount, + writeptype writeproc, void *writeparm, char *eol, char *urlprefix ) +{ + char **dnrdns; + int i; + + if (( dnrdns = ldap_explode_dn( dn, 1 )) == NULL ) { + return( -1 ); + } + + if ( urlprefix != NULL ) { + sprintf( buf, "
" ); + } else if ( width > 0 ) { + sprintf( buf, "%-*s", width, " " ); + } else { + *buf = '\0'; + } + + for ( i = 0; dnrdns[ i ] != NULL && ( rdncount == 0 || i < rdncount ); + ++i ) { + if ( i > 0 ) { + strcat( buf, ", " ); + } + strcat( buf, dnrdns[ i ] ); + } + + if ( urlprefix != NULL ) { + strcat( buf, "
" ); + } + + ldap_value_free( dnrdns ); + + strcat( buf, eol ); + + return ((*writeproc)( writeparm, buf, strlen( buf ))); +} + + + +#define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \ + ( c >= '@' && c <= 'Z' ) || \ + ( c == '_' ) || \ + ( c >= 'a' && c <= 'z' )) + +static void +strcat_escaped( char *s1, char *s2 ) +{ + char *p, *q; + char *hexdig = "0123456789ABCDEF"; + + p = s1 + strlen( s1 ); + for ( q = s2; *q != '\0'; ++q ) { + if ( HREF_CHAR_ACCEPTABLE( *q )) { + *p++ = *q; + } else { + *p++ = '%'; + *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ]; + *p++ = hexdig[ 0x0F & *q ]; + } + } + + *p = '\0'; +} + + +#define GET2BYTENUM( p ) (( *p - '0' ) * 10 + ( *(p+1) - '0' )) + +static char * +time2text( char *ldtimestr, int dateonly ) +{ + int len; + struct tm t; + char *p, *timestr, zone, *fmterr = "badly formatted time"; + time_t gmttime; +/* CTIME for this platform doesn't use this. */ +#if !defined(SUNOS4) && !defined(BSDI) && !defined(LINUX1_2) && \ + !defined(SNI) && !defined(_WIN32) && !defined(macintosh) && !defined(LINUX) + char buf[26]; +#endif + + memset( (char *)&t, 0, sizeof( struct tm )); + if (( len = (int)strlen( ldtimestr )) < 13 ) { + return( fmterr ); + } + if ( len > 15 ) { /* throw away excess from 4-digit year time string */ + len = 15; + } else if ( len == 14 ) { + len = 13; /* assume we have a time w/2-digit year (len=13) */ + } + + for ( p = ldtimestr; p - ldtimestr + 1 < len; ++p ) { + if ( !isdigit( *p )) { + return( fmterr ); + } + } + + p = ldtimestr; + t.tm_year = GET2BYTENUM( p ); p += 2; + if ( len == 15 ) { + t.tm_year = 100 * (t.tm_year - 19); + t.tm_year += GET2BYTENUM( p ); p += 2; + } + else { + /* 2 digit years...assumed to be in the range (19)70 through + (20)69 ...less than 70 (for now, 38) means 20xx */ + if(t.tm_year < 70) { + t.tm_year += 100; + } + } + t.tm_mon = GET2BYTENUM( p ) - 1; p += 2; + t.tm_mday = GET2BYTENUM( p ); p += 2; + t.tm_hour = GET2BYTENUM( p ); p += 2; + t.tm_min = GET2BYTENUM( p ); p += 2; + t.tm_sec = GET2BYTENUM( p ); p += 2; + + if (( zone = *p ) == 'Z' ) { /* GMT */ + zone = '\0'; /* no need to indicate on screen, so we make it null */ + } + + gmttime = gtime( &t ); + timestr = NSLDAPI_CTIME( &gmttime, buf, sizeof(buf) ); + + timestr[ strlen( timestr ) - 1 ] = zone; /* replace trailing newline */ + if ( dateonly ) { + strcpy( timestr + 11, timestr + 20 ); + } + + return( timestr ); +} + + + +/* gtime.c - inverse gmtime */ + +#if !defined( macintosh ) && !defined( _WINDOWS ) && !defined( DOS ) && !defined(XP_OS2) +#include +#endif /* !macintosh */ + +/* gtime(): the inverse of localtime(). + This routine was supplied by Mike Accetta at CMU many years ago. + */ + +static int dmsize[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define dysize(y) \ + (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366))) + +/* +#define YEAR(y) ((y) >= 100 ? (y) : (y) + 1900) +*/ +#define YEAR(y) (((y) < 1900) ? ((y) + 1900) : (y)) + +/* */ + +static long gtime ( struct tm *tm ) +{ + register int i, + sec, + mins, + hour, + mday, + mon, + year; + register long result; + + if ((sec = tm -> tm_sec) < 0 || sec > 59 + || (mins = tm -> tm_min) < 0 || mins > 59 + || (hour = tm -> tm_hour) < 0 || hour > 24 + || (mday = tm -> tm_mday) < 1 || mday > 31 + || (mon = tm -> tm_mon + 1) < 1 || mon > 12) + return ((long) -1); + if (hour == 24) { + hour = 0; + mday++; + } + year = YEAR (tm -> tm_year); + + result = 0L; + for (i = 1970; i < year; i++) + result += dysize (i); + if (dysize (year) == 366 && mon >= 3) + result++; + while (--mon) + result += dmsize[mon - 1]; + result += mday - 1; + result = 24 * result + hour; + result = 60 * result + mins; + result = 60 * result + sec; + + return result; +} + +static int +searchaction( LDAP *ld, char *buf, char *base, LDAPMessage *entry, char *dn, + struct ldap_tmplitem *tip, int labelwidth, int rdncount, + writeptype writeproc, void *writeparm, char *eol, char *urlprefix ) +{ + int err = LDAP_SUCCESS, lderr, i, count, html; + char **vals, **members; + char *value, *filtpattern, *attr, *selectname; + char *retattrs[2], filter[ 256 ]; + LDAPMessage *ldmp; + struct timeval timeout; + + html = ( urlprefix != NULL ); + + for ( i = 0; tip->ti_args != NULL && tip->ti_args[ i ] != NULL; ++i ) { + ; + } + if ( i < 3 ) { + return( LDAP_PARAM_ERROR ); + } + attr = tip->ti_args[ 0 ]; + filtpattern = tip->ti_args[ 1 ]; + retattrs[ 0 ] = tip->ti_args[ 2 ]; + retattrs[ 1 ] = NULL; + selectname = tip->ti_args[ 3 ]; + + vals = NULL; + if ( attr == NULL ) { + value = NULL; + } else if ( strcasecmp( attr, "-dnb" ) == 0 ) { + return( LDAP_PARAM_ERROR ); + } else if ( strcasecmp( attr, "-dnt" ) == 0 ) { + value = dn; + } else if (( vals = ldap_get_values( ld, entry, attr )) != NULL ) { + value = vals[ 0 ]; + } else { + value = NULL; + } + + ldap_build_filter( filter, sizeof( filter ), filtpattern, NULL, NULL, NULL, + value, NULL ); + + if ( html ) { + /* + * if we are generating HTML, we add an HREF link that embodies this + * search action as an LDAP URL, instead of actually doing the search + * now. + */ + sprintf( buf, "
%s

%s", + tip->ti_label, eol ); + if ((*writeproc)( writeparm, buf, strlen( buf )) < 0 ) { + return( LDAP_LOCAL_ERROR ); + } + return( LDAP_SUCCESS ); + } + + timeout.tv_sec = SEARCH_TIMEOUT_SECS; + timeout.tv_usec = 0; + +#ifdef CLDAP + if ( LDAP_IS_CLDAP( ld )) + lderr = cldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filter, retattrs, + 0, &ldmp, NULL ); + else +#endif /* CLDAP */ + lderr = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE, filter, + retattrs, 0, &timeout, &ldmp ); + + if ( lderr == LDAP_SUCCESS || NONFATAL_LDAP_ERR( lderr )) { + if (( count = ldap_count_entries( ld, ldmp )) > 0 ) { + if (( members = (char **)NSLDAPI_MALLOC( (count + 1) + * sizeof(char *))) == NULL ) { + err = LDAP_NO_MEMORY; + } else { + for ( i = 0, entry = ldap_first_entry( ld, ldmp ); + entry != NULL; + entry = ldap_next_entry( ld, entry ), ++i ) { + members[ i ] = ldap_get_dn( ld, entry ); + } + members[ i ] = NULL; + + ldap_sort_values(ld,members, ldap_sort_strcasecmp); + + err = do_vals2text( ld, NULL, members, tip->ti_label, + html ? -1 : 0, LDAP_SYN_DN, writeproc, writeparm, + eol, rdncount, urlprefix ); + + ldap_value_free( members ); + } + } + ldap_msgfree( ldmp ); + } + + + if ( vals != NULL ) { + ldap_value_free( vals ); + } + + return(( err == LDAP_SUCCESS ) ? lderr : err ); +} diff --git a/ldap/c-sdk/libraries/libldap/tmpltest.c b/ldap/c-sdk/libraries/libldap/tmpltest.c new file mode 100644 index 0000000000..35ee54e06d --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/tmpltest.c @@ -0,0 +1,319 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* tmpltest.c - implements a test/config templates. */ +#include +#include + +#ifdef _WINDOWS +#include +#endif + +#include "ldap-int.h" +#include "disptmpl.h" +#include "srchpref.h" + +#ifdef MACOS +#include +#include +#endif /* MACOS */ + +#ifdef NEEDPROTOS +void dump_tmpl( struct ldap_disptmpl *tmpl ); +void dump_srchpref( struct ldap_searchobj *sp ); +#else /* NEEDPROTOS */ +void dump_tmpl(); +void dump_srchpref(); +#endif /* NEEDPROTOS */ + + +#define NULLSTRINGIFNULL( s ) ( s == NULL ? "(null)" : s ) + + +int +main( int argc, char **argv ) +{ + struct ldap_disptmpl *templates, *dtp; + struct ldap_searchobj *so, *sop; + int err; + +#ifdef MACOS + ccommand( &argv ); + for ( argc = 0; argv[ argc ] != NULL; ++argc ) { + ; + } + cshow( stdout ); +#endif /* MACOS */ + + if (( err = ldap_init_templates( "ldaptemplates.conf", &templates )) + != 0 ) { + fprintf( stderr, "ldap_init_templates failed (%d)\n", err ); + exit( 1 ); + } + + if (( err = ldap_init_searchprefs( "ldapsearchprefs.conf", &so )) + != 0 ) { + fprintf( stderr, "ldap_init_searchprefs failed (%d)\n", err ); + exit( 1 ); + } + + if ( argc == 1 ) { + printf( "*** Display Templates:\n" ); + for ( dtp = ldap_first_disptmpl( templates ); dtp != NULLDISPTMPL; + dtp = ldap_next_disptmpl( templates, dtp )) { + dump_tmpl( dtp ); + printf( "\n\n" ); + } + + printf( "\n\n*** Search Objects:\n" ); + for ( sop = ldap_first_searchobj( so ); sop != NULLSEARCHOBJ; + sop = ldap_next_searchobj( so, sop )) { + dump_srchpref( sop ); + printf( "\n\n" ); + } + + } else { + if (( dtp = ldap_oc2template( ++argv, templates )) == NULL ) { + fprintf( stderr, "no matching template found\n" ); + } else { + dump_tmpl( dtp ); + } + } + + + ldap_free_templates( templates ); + ldap_free_searchprefs( so ); + + exit( 0 ); +} + + +static char *syn_name[] = { + "?", "CIS", "MLS", "DN", "BOOL", "JPEG", "JPEGBTN", "FAX", "FAXBTN", + "AUDIOBTN", "TIME", "DATE", "URL", "SEARCHACT", "LINKACT", "ADDDNACT", + "VERIFYACT", +}; + +static char *syn_type[] = { + "?", "txt", "img", "?", "bool", "?", "?", "?", "btn", + "?", "?", "?", "?", "?", "?", "?", + "action", "?" +}; + +static char *includeattrs[] = { "objectClass", "sn", NULL }; + +static char *item_opts[] = { + "ro", "sort", "1val", "hide", "required", "hideiffalse", NULL +}; + +static unsigned long item_opt_vals[] = { + LDAP_DITEM_OPT_READONLY, LDAP_DITEM_OPT_SORTVALUES, + LDAP_DITEM_OPT_SINGLEVALUED, LDAP_DITEM_OPT_HIDEIFEMPTY, + LDAP_DITEM_OPT_VALUEREQUIRED, LDAP_DITEM_OPT_HIDEIFFALSE, +}; + + +void +dump_tmpl( struct ldap_disptmpl *tmpl ) +{ + struct ldap_tmplitem *rowp, *colp; + int i, rowcnt, colcnt; + char **fetchattrs; + struct ldap_oclist *ocp; + struct ldap_adddeflist *adp; + + printf( "** Template \"%s\" (plural \"%s\", icon \"%s\")\n", + NULLSTRINGIFNULL( tmpl->dt_name ), + NULLSTRINGIFNULL( tmpl->dt_pluralname ), + NULLSTRINGIFNULL( tmpl->dt_iconname )); + + printf( "object class list:\n" ); + for ( ocp = tmpl->dt_oclist; ocp != NULL; ocp = ocp->oc_next ) { + for ( i = 0; ocp->oc_objclasses[ i ] != NULL; ++i ) { + printf( "%s%s", i == 0 ? " " : " & ", + NULLSTRINGIFNULL( ocp->oc_objclasses[ i ] )); + } + putchar( '\n' ); + } + putchar( '\n' ); + + printf( "template options: " ); + if ( tmpl->dt_options == 0L ) { + printf( "NONE\n" ); + } else { + printf( "%s %s %s\n", LDAP_IS_DISPTMPL_OPTION_SET( tmpl, + LDAP_DTMPL_OPT_ADDABLE ) ? "addable" : "", + LDAP_IS_DISPTMPL_OPTION_SET( tmpl, LDAP_DTMPL_OPT_ALLOWMODRDN ) + ? "modrdn" : "", + LDAP_IS_DISPTMPL_OPTION_SET( tmpl, LDAP_DTMPL_OPT_ALTVIEW ) + ? "altview" : "" ); + } + + printf( "authenticate as attribute: %s\n", tmpl->dt_authattrname != NULL ? + tmpl->dt_authattrname : "" ); + + printf( "default RDN attribute: %s\n", tmpl->dt_defrdnattrname != NULL ? + tmpl->dt_defrdnattrname : "NONE" ); + + printf( "default add location: %s\n", tmpl->dt_defaddlocation != NULL ? + tmpl->dt_defaddlocation : "NONE" ); + + printf( "\nnew entry value default rules:\n" ); + for ( adp = tmpl->dt_adddeflist; adp != NULL; adp = adp->ad_next ) { + if ( adp->ad_source == LDAP_ADSRC_CONSTANTVALUE ) { + printf( " attribute %s <-- constant value \"%s\"\n", + NULLSTRINGIFNULL( adp->ad_attrname), + NULLSTRINGIFNULL( adp->ad_value )); + } else { + printf( " attribute %s <-- adder's DN\n", + NULLSTRINGIFNULL( adp->ad_attrname )); + } + } + putchar( '\n' ); + + printf( "\nfetch attributes & values:\n" ); + if (( fetchattrs = ldap_tmplattrs( tmpl, includeattrs, 1, + LDAP_SYN_OPT_DEFER )) == NULL ) { + printf( " \n" ); + } else { + for ( i = 0; fetchattrs[ i ] != NULL; ++i ) { + printf( " %s\n", fetchattrs[ i ] ); + free( fetchattrs[ i ] ); + } + free( (char *)fetchattrs ); + } + + printf( "\nfetch attributes only:\n" ); + if (( fetchattrs = ldap_tmplattrs( tmpl, NULL, 0, + LDAP_SYN_OPT_DEFER )) == NULL ) { + printf( " \n" ); + } else { + for ( i = 0; fetchattrs[ i ] != NULL; ++i ) { + printf( " %s\n", fetchattrs[ i ] ); + free( fetchattrs[ i ] ); + } + free( (char *)fetchattrs ); + } + + printf( "\ntemplate items:\n" ); + rowcnt = 0; + for ( rowp = ldap_first_tmplrow( tmpl ); rowp != NULLTMPLITEM; + rowp = ldap_next_tmplrow( tmpl, rowp )) { + ++rowcnt; + colcnt = 0; + for ( colp = ldap_first_tmplcol( tmpl, rowp ); colp != NULLTMPLITEM; + colp = ldap_next_tmplcol( tmpl, rowp, colp )) { + ++colcnt; + printf( " %2d-%d: %s (%s%s", rowcnt, colcnt, + syn_name[ colp->ti_syntaxid & 0x0000FFFF ], + syn_type[ LDAP_GET_SYN_TYPE( colp->ti_syntaxid ) >> 24 ], + (( LDAP_GET_SYN_OPTIONS( colp->ti_syntaxid ) & + LDAP_SYN_OPT_DEFER ) != 0 ) ? ",defer" : "" ); + + for ( i = 0; item_opts[ i ] != NULL; ++i ) { + if ( LDAP_IS_TMPLITEM_OPTION_SET( colp, item_opt_vals[ i ] )) { + printf( ",%s", NULLSTRINGIFNULL( item_opts[ i ] )); + } + } + + printf( "), %s, %s", NULLSTRINGIFNULL( colp->ti_attrname ), + NULLSTRINGIFNULL( colp->ti_label )); + if ( colp->ti_args != NULL ) { + printf( ",args=" ); + for ( i = 0; colp->ti_args[ i ] != NULL; ++i ) { + printf( "<%s>", NULLSTRINGIFNULL( colp->ti_args[ i ] )); + } + } + + putchar( '\n' ); + } + } +} + + +void +dump_srchpref( struct ldap_searchobj *so ) +{ + int i; + struct ldap_searchattr *sa; + struct ldap_searchmatch *sm; + + printf( "Object type prompt: %s\n", + NULLSTRINGIFNULL( so->so_objtypeprompt )); + printf( "Options: %s\n", + LDAP_IS_SEARCHOBJ_OPTION_SET( so, LDAP_SEARCHOBJ_OPT_INTERNAL ) ? + "internal" : "NONE" ); + printf( "Prompt: %s\n", NULLSTRINGIFNULL( so->so_prompt )); + printf( "Scope: " ); + switch ( so->so_defaultscope ) { + case LDAP_SCOPE_BASE: + printf( "LDAP_SCOPE_BASE" ); + break; + case LDAP_SCOPE_ONELEVEL: + printf( "LDAP_SCOPE_ONELEVEL" ); + break; + case LDAP_SCOPE_SUBTREE: + printf( "LDAP_SCOPE_SUBTREE" ); + break; + default: + printf("*** unknown!" ); + } + puts( "\n" ); + printf( "Filter prefix: %s\n", + NULLSTRINGIFNULL( so->so_filterprefix )); + printf( "Filter tag: %s\n", + NULLSTRINGIFNULL( so->so_filtertag )); + printf( "Default select attr: %s\n", + NULLSTRINGIFNULL( so->so_defaultselectattr )); + printf( "Default select text: %s\n", + NULLSTRINGIFNULL( so->so_defaultselecttext )); + printf( "Searchable attributes ---- \n" ); + for ( sa = so->so_salist; sa != NULL; sa = sa->sa_next ) { + printf( " Label: %s\n", NULLSTRINGIFNULL( sa->sa_attrlabel )); + printf( " Attribute: %s\n", NULLSTRINGIFNULL( sa->sa_attr )); + printf( " Select attr: %s\n", NULLSTRINGIFNULL( sa->sa_selectattr )); + printf( " Select text: %s\n", NULLSTRINGIFNULL( sa->sa_selecttext )); + printf( " Match types ---- \n" ); + for ( i = 0, sm = so->so_smlist; sm != NULL; i++, sm = sm->sm_next ) { + if (( sa->sa_matchtypebitmap >> i ) & 1 ) { + printf( " %s (%s)\n", + NULLSTRINGIFNULL( sm->sm_matchprompt ), + NULLSTRINGIFNULL( sm->sm_filter )); + } + } + } +} diff --git a/ldap/c-sdk/libraries/libldap/ufn.c b/ldap/c-sdk/libraries/libldap/ufn.c new file mode 100644 index 0000000000..54e737e158 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/ufn.c @@ -0,0 +1,563 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * ufn.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +typedef int (LDAP_CALL *cancelptype)( void *cancelparm ); + +static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, + char *prefix, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, + char *tag1, char *tag2, char *tag3 ); +static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b ); +static LDAPMessage *ldap_ufn_expand( LDAP *ld, + LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, char **dns, + char *filter, int scope, char **attrs, int aonly, int *err ); + +/* + * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature; + * specify ldapfilter.conf tags for each phase of search + * + * ld LDAP descriptor + * ufncomp the exploded user friendly name to look for + * ncomp number of elements in ufncomp + * prefix where to start searching + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * res will contain the result of the search + * cancelproc routine that returns non-zero if operation should be + * cancelled. This can be NULL. If it is non-NULL, the + * routine will be called periodically. + * cancelparm void * that is passed to cancelproc + * tag[123] the ldapfilter.conf tag that will be used in phases + * 1, 2, and 3 of the search, respectively + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * char *ufncomp[] = { "howes", "umich", "us", 0 } + * LDAPMessage *res; + * error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly, + * &res, acancelproc, along, "ufn first", + * "ufn intermediate", "ufn last" ); + */ + +static int +ldap_ufn_search_ctx( + LDAP *ld, + char **ufncomp, + int ncomp, + char *prefix, + char **attrs, + int attrsonly, + LDAPMessage **res, + LDAP_CANCELPROC_CALLBACK *cancelproc, + void *cancelparm, + char *tag1, + char *tag2, + char *tag3 +) +{ + char *dn, *ftag = NULL; + char **dns = NULL; + int max, i, err, scope = 0, phase, tries; + LDAPFiltInfo *fi; + LDAPMessage *tmpcand; + LDAPMessage *candidates; + static char *objattrs[] = { "objectClass", NULL }; + + /* + * look up ufn components from most to least significant. + * there are 3 phases. + * phase 1 search the root for orgs or countries + * phase 2 search for orgs + * phase 3 search for a person + * in phases 1 and 2, we are building a list of candidate DNs, + * below which we will search for the final component of the ufn. + * for each component we try the filters listed in the + * filterconfig file, first one-level (except the last compoment), + * then subtree. if any of them produce any results, we go on to + * the next component. + */ + + *res = NULL; + candidates = NULL; + phase = 1; + for ( ncomp--; ncomp != -1; ncomp-- ) { + if ( *ufncomp[ncomp] == '"' ) { + char *quote; + + if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL ) + *quote = '\0'; + strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 ); + } + if ( ncomp == 0 ) + phase = 3; + + switch ( phase ) { + case 1: + ftag = tag1; + scope = LDAP_SCOPE_ONELEVEL; + break; + case 2: + ftag = tag2; + scope = LDAP_SCOPE_ONELEVEL; + break; + case 3: + ftag = tag3; + scope = LDAP_SCOPE_SUBTREE; + break; + } + + /* + * construct an array of DN's to search below from the + * list of candidates. + */ + + if ( candidates == NULL ) { + if ( prefix != NULL ) { + if ( (dns = (char **)NSLDAPI_MALLOC( + sizeof(char *) * 2 )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + dns[0] = nsldapi_strdup( prefix ); + dns[1] = NULL; + } else { + dns = NULL; + } + } else { + i = 0, max = 0; + for ( tmpcand = candidates; tmpcand != NULL && + tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT; + tmpcand = tmpcand->lm_chain ) + { + if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL ) + continue; + + if ( dns == NULL ) { + if ( (dns = (char **)NSLDAPI_MALLOC( + sizeof(char *) * 8 )) == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, + NULL, NULL ); + return( err ); + } + max = 8; + } else if ( i >= max ) { + if ( (dns = (char **)NSLDAPI_REALLOC( + dns, sizeof(char *) * 2 * max )) + == NULL ) { + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, + NULL, NULL ); + return( err ); + } + max *= 2; + } + dns[i++] = dn; + dns[i] = NULL; + } + ldap_msgfree( candidates ); + candidates = NULL; + } + tries = 0; + tryagain: + tries++; + for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag, + ufncomp[ncomp] ); fi != NULL; + fi = ldap_getnextfilter( ld->ld_filtd ) ) + { + if ( (candidates = ldap_ufn_expand( ld, cancelproc, + cancelparm, dns, fi->lfi_filter, scope, + phase == 3 ? attrs : objattrs, + phase == 3 ? attrsonly : 1, &err )) != NULL ) + { + break; + } + + if ( err == -1 || err == LDAP_USER_CANCELLED ) { + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + return( err ); + } + } + + if ( candidates == NULL ) { + if ( tries < 2 && phase != 3 ) { + scope = LDAP_SCOPE_SUBTREE; + goto tryagain; + } else { + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + return( err ); + } + } + + /* go on to the next component */ + if ( phase == 1 ) + phase++; + if ( dns != NULL ) { + ldap_value_free( dns ); + dns = NULL; + } + } + *res = candidates; + + return( err ); +} + +int +LDAP_CALL +ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm, + char *tag1, char *tag2, char *tag3 ) +{ + char **ufncomp, **prefixcomp; + char *pbuf; + int ncomp, pcomp, i, err = 0; + + /* getfilter stuff must be inited before we are called */ + if ( ld->ld_filtd == NULL ) { + err = LDAP_PARAM_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + /* call ldap_explode_dn() to break the ufn into its components */ + if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL ) { + err = LDAP_LOCAL_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ ) + ; /* NULL */ + + /* more than two components => try it fully qualified first */ + if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) { + err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs, + attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 ); + + if ( ldap_count_entries( ld, *res ) > 0 ) { + ldap_value_free( ufncomp ); + return( err ); + } else { + ldap_msgfree( *res ); + *res = NULL; + } + } + + if ( ld->ld_ufnprefix == NULL ) { + ldap_value_free( ufncomp ); + return( err ); + } + + /* if that failed, or < 2 components, use the prefix */ + if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) { + ldap_value_free( ufncomp ); + err = LDAP_LOCAL_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ ) + ; /* NULL */ + if ( (pbuf = (char *)NSLDAPI_MALLOC( strlen( ld->ld_ufnprefix ) + 1 )) + == NULL ) { + ldap_value_free( ufncomp ); + ldap_value_free( prefixcomp ); + err = LDAP_NO_MEMORY; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + for ( i = 0; i < pcomp; i++ ) { + int j; + + *pbuf = '\0'; + for ( j = i; j < pcomp; j++ ) { + strcat( pbuf, prefixcomp[j] ); + if ( j + 1 < pcomp ) + strcat( pbuf, "," ); + } + err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs, + attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 ); + + if ( ldap_count_entries( ld, *res ) > 0 ) { + break; + } else { + ldap_msgfree( *res ); + *res = NULL; + } + } + + ldap_value_free( ufncomp ); + ldap_value_free( prefixcomp ); + NSLDAPI_FREE( pbuf ); + + return( err ); +} + +/* + * same as ldap_ufn_search_ct, except without the ability to specify + * ldapfilter.conf tags. + */ +int +LDAP_CALL +ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res, LDAP_CANCELPROC_CALLBACK *cancelproc, void *cancelparm ) +{ + return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc, + cancelparm, "ufn first", "ufn intermediate", "ufn last" ) ); +} + +/* + * same as ldap_ufn_search_c without the cancel function + */ +int +LDAP_CALL +ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly, + LDAPMessage **res ) +{ + struct timeval tv; + + tv.tv_sec = ld->ld_timelimit; + + return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, + ld->ld_timelimit ? ldap_ufn_timeout : NULL, + ld->ld_timelimit ? (void *) &tv : NULL, + "ufn first", "ufn intermediate", "ufn last" ) ); +} + + +/* + * ldap_msg_merge - merge two ldap search result chains. the more + * serious of the two error result codes is kept. + */ + +static LDAPMessage * +ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b ) +{ + LDAPMessage *end, *aprev, *aend, *bprev, *bend; + + if ( a == NULL ) + return( b ); + + if ( b == NULL ) + return( a ); + + /* find the ends of the a and b chains */ + aprev = NULL; + for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain ) + aprev = aend; + bprev = NULL; + for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain ) + bprev = bend; + + /* keep result a */ + if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) { + /* remove result b */ + ldap_msgfree( bend ); + if ( bprev != NULL ) + bprev->lm_chain = NULL; + else + b = NULL; + end = aend; + if ( aprev != NULL ) + aprev->lm_chain = NULL; + else + a = NULL; + /* keep result b */ + } else { + /* remove result a */ + ldap_msgfree( aend ); + if ( aprev != NULL ) + aprev->lm_chain = NULL; + else + a = NULL; + end = bend; + if ( bprev != NULL ) + bprev->lm_chain = NULL; + else + b = NULL; + } + + if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) || + (b == NULL && aprev == NULL) ) + return( end ); + + if ( a == NULL ) { + bprev->lm_chain = end; + return( b ); + } else if ( b == NULL ) { + aprev->lm_chain = end; + return( a ); + } else { + bprev->lm_chain = end; + aprev->lm_chain = b; + return( a ); + } +} + +static LDAPMessage * +ldap_ufn_expand( LDAP *ld, LDAP_CANCELPROC_CALLBACK *cancelproc, + void *cancelparm, char **dns, char *filter, int scope, + char **attrs, int aonly, int *err ) +{ + LDAPMessage *tmpcand, *tmpres; + char *dn; + int i, msgid; + struct timeval tv; + + /* search for this component below the current candidates */ + tmpcand = NULL; + i = 0; + do { + if ( dns != NULL ) + dn = dns[i]; + else + dn = ""; + + if (( msgid = ldap_search( ld, dn, scope, filter, attrs, + aonly )) == -1 ) { + ldap_msgfree( tmpcand ); + tmpcand = NULL; + *err = LDAP_GET_LDERRNO( ld, NULL, NULL ); + /* + * Compiling with gcc-4.2 on Mac: + * gcc-4.2 -arch ppc -c -o ufn.o -gdwarf-2 -01 ufn.c + * having a return NULL statement here causes gcc to + * hang. Therefore set tmpcand to null (above) and break + * out of this loop to make gcc happy. + */ + break; + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; /* 1/10 of a second */ + + do { + *err = ldap_result( ld, msgid, 1, &tv, &tmpres ); + if ( *err == 0 && cancelproc != NULL && + (*cancelproc)( cancelparm ) != 0 ) { + ldap_abandon( ld, msgid ); + *err = LDAP_USER_CANCELLED; + LDAP_SET_LDERRNO( ld, *err, NULL, NULL ); + } + } while ( *err == 0 ); + + if ( *err == LDAP_USER_CANCELLED || *err < 0 || + ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) { + ldap_msgfree( tmpcand ); + return( NULL ); + } + + tmpcand = ldap_msg_merge( ld, tmpcand, tmpres ); + + i++; + } while ( dns != NULL && dns[i] != NULL ); + + /* Catch the tmpcand = NULL case as required by breaking out the loop + * to prevent gcc-4.2 hanging on Mac. + */ + if (!tmpcand) + return NULL; + + if ( ldap_count_entries( ld, tmpcand ) > 0 ) { + return( tmpcand ); + } else { + ldap_msgfree( tmpcand ); + return( NULL ); + } +} + +/* + * ldap_ufn_setfilter - set the filter config file used in ufn searching + */ + +LDAPFiltDesc * +LDAP_CALL +ldap_ufn_setfilter( LDAP *ld, char *fname ) +{ + if ( ld->ld_filtd != NULL ) + ldap_getfilter_free( ld->ld_filtd ); + + return( ld->ld_filtd = ldap_init_getfilter( fname ) ); +} + +void +LDAP_CALL +ldap_ufn_setprefix( LDAP *ld, char *prefix ) +{ + if ( ld->ld_ufnprefix != NULL ) + NSLDAPI_FREE( ld->ld_ufnprefix ); + + ld->ld_ufnprefix = nsldapi_strdup( prefix ); +} + +int +LDAP_C +ldap_ufn_timeout( void *tvparam ) +{ + struct timeval *tv; + + tv = (struct timeval *)tvparam; + + if ( tv->tv_sec != 0 ) { + tv->tv_usec = tv->tv_sec * 1000000; /* sec => micro sec */ + tv->tv_sec = 0; + } + tv->tv_usec -= 100000; /* 1/10 of a second */ + + return( tv->tv_usec <= 0 ? 1 : 0 ); +} diff --git a/ldap/c-sdk/libraries/libldap/unbind.c b/ldap/c-sdk/libraries/libldap/unbind.c new file mode 100644 index 0000000000..95ca49411f --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/unbind.c @@ -0,0 +1,248 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * unbind.c + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + +int +LDAP_CALL +ldap_unbind( LDAP *ld ) +{ + LDAPDebug( LDAP_DEBUG_TRACE, "ldap_unbind\n", 0, 0, 0 ); + + return( ldap_ld_free( ld, NULL, NULL, 1 ) ); +} + + +int +LDAP_CALL +ldap_unbind_s( LDAP *ld ) +{ + return( ldap_ld_free( ld, NULL, NULL, 1 )); +} + + +int +LDAP_CALL +ldap_unbind_ext( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + return( ldap_ld_free( ld, serverctrls, clientctrls, 1 )); +} + + +/* + * Dispose of the LDAP session ld, including all associated connections + * and resources. If close is non-zero, an unbind() request is sent as well. + */ +int +ldap_ld_free( LDAP *ld, LDAPControl **serverctrls, + LDAPControl **clientctrls, int close ) +{ + LDAPMessage *lm, *next; + int err = LDAP_SUCCESS; + LDAPRequest *lr, *nextlr; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ld->ld_sbp->sb_naddr == 0 ) { + LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); + /* free LDAP structure and outstanding requests/responses */ + for ( lr = ld->ld_requests; lr != NULL; lr = nextlr ) { + nextlr = lr->lr_next; + nsldapi_free_request( ld, lr, 0 ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); + + /* free and unbind from all open connections */ + LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); + while ( ld->ld_conns != NULL ) { + nsldapi_free_connection( ld, ld->ld_conns, serverctrls, + clientctrls, 1, close ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); + + } else { + int i; + + for ( i = 0; i < ld->ld_sbp->sb_naddr; ++i ) { + NSLDAPI_FREE( ld->ld_sbp->sb_addrs[ i ] ); + } + NSLDAPI_FREE( ld->ld_sbp->sb_addrs ); + NSLDAPI_FREE( ld->ld_sbp->sb_fromaddr ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_RESP_LOCK ); + for ( lm = ld->ld_responses; lm != NULL; lm = next ) { + next = lm->lm_next; + ldap_msgfree( lm ); + } + LDAP_MUTEX_UNLOCK( ld, LDAP_RESP_LOCK ); + + /* call cache unbind function to allow it to clean up after itself */ + if ( ld->ld_cache_unbind != NULL ) { + LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK ); + (void)ld->ld_cache_unbind( ld, 0, 0 ); + LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK ); + } + + /* call the dispose handle I/O callback if one is defined */ + if ( ld->ld_extdisposehandle_fn != NULL ) { + /* + * We always pass the session extended I/O argument to + * the dispose handle callback. + */ + ld->ld_extdisposehandle_fn( ld, ld->ld_ext_session_arg ); + } + + if ( ld->ld_error != NULL ) + NSLDAPI_FREE( ld->ld_error ); + if ( ld->ld_matched != NULL ) + NSLDAPI_FREE( ld->ld_matched ); + if ( ld->ld_host != NULL ) + NSLDAPI_FREE( ld->ld_host ); + if ( ld->ld_ufnprefix != NULL ) + NSLDAPI_FREE( ld->ld_ufnprefix ); + if ( ld->ld_filtd != NULL ) + ldap_getfilter_free( ld->ld_filtd ); + if ( ld->ld_abandoned != NULL ) + NSLDAPI_FREE( ld->ld_abandoned ); + if ( ld->ld_sbp != NULL ) + ber_sockbuf_free( ld->ld_sbp ); + if ( ld->ld_defhost != NULL ) + NSLDAPI_FREE( ld->ld_defhost ); + if ( ld->ld_servercontrols != NULL ) + ldap_controls_free( ld->ld_servercontrols ); + if ( ld->ld_clientcontrols != NULL ) + ldap_controls_free( ld->ld_clientcontrols ); + if ( ld->ld_preferred_language != NULL ) + NSLDAPI_FREE( ld->ld_preferred_language ); + nsldapi_iostatus_free( ld ); +#ifdef LDAP_SASLIO_HOOKS + NSLDAPI_FREE( ld->ld_def_sasl_mech ); + NSLDAPI_FREE( ld->ld_def_sasl_realm ); + NSLDAPI_FREE( ld->ld_def_sasl_authcid ); + NSLDAPI_FREE( ld->ld_def_sasl_authzid ); +#endif + + /* + * XXXmcs: should use cache function pointers to hook in memcache + */ + if ( ld->ld_memcache != NULL ) { + ldap_memcache_set( ld, NULL ); + } + + /* free all mutexes we have allocated */ + nsldapi_mutex_free_all( ld ); + NSLDAPI_FREE( ld->ld_mutex ); + + NSLDAPI_FREE( (char *) ld ); + + return( err ); +} + + + +int +nsldapi_send_unbind( LDAP *ld, Sockbuf *sb, LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ + BerElement *ber; + int err, msgid; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_unbind\n", 0, 0, 0 ); + + /* create a message to send */ + if (( err = nsldapi_alloc_ber_with_options( ld, &ber )) + != LDAP_SUCCESS ) { + return( err ); + } + + /* fill it in */ + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + if ( ber_printf( ber, "{itn", msgid, LDAP_REQ_UNBIND ) == -1 ) { + ber_free( ber, 1 ); + err = LDAP_ENCODING_ERROR; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + + if (( err = nsldapi_put_controls( ld, serverctrls, 1, ber )) + != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( err ); + } + + /* send the message */ + err = nsldapi_send_ber_message( ld, sb, ber, 1 /* free ber */, + 0 /* will not handle EPIPE */ ); + if ( err != 0 ) { + ber_free( ber, 1 ); + if (err != -2 ) { + /* + * Message could not be sent, and the reason is + * something other than a "would block" error. + * Note that we ignore "would block" errors because + * it is not critical that an UnBind request + * actually reach the LDAP server. + */ + err = LDAP_SERVER_DOWN; + LDAP_SET_LDERRNO( ld, err, NULL, NULL ); + return( err ); + } + } + + return( LDAP_SUCCESS ); +} diff --git a/ldap/c-sdk/libraries/libldap/unescape.c b/ldap/c-sdk/libraries/libldap/unescape.c new file mode 100644 index 0000000000..66d5bd27ff --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/unescape.c @@ -0,0 +1,84 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * LIBLDAP unescape.c -- LDAP URL un-escape routines + * We also tolerate URLs that look like: and + */ + +#include "ldap-int.h" + + +static int unhex( char c ); + + +void +nsldapi_hex_unescape( char *s ) +{ +/* + * Remove URL hex escapes from s... done in place. The basic concept for + * this routine is borrowed from the WWW library HTUnEscape() routine. + */ + char *p; + + for ( p = s; *s != '\0'; ++s ) { + if ( *s == '%' ) { + if ( *++s == '\0' ) { + break; + } + *p = unhex( *s ) << 4; + if ( *++s == '\0' ) { + break; + } + *p++ += unhex( *s ); + + } else { + *p++ = *s; + } + } + + *p = '\0'; +} + + +static int +unhex( char c ) +{ + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} diff --git a/ldap/c-sdk/libraries/libldap/url.c b/ldap/c-sdk/libraries/libldap/url.c new file mode 100644 index 0000000000..789ee3ac2a --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/url.c @@ -0,0 +1,527 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + * + */ +/* LIBLDAP url.c -- LDAP URL related routines + * + * LDAP URLs look like this: + * l d a p : / / [ hostport ] [ / dn [ ? [ attributes ] [ ? [ scope ] + * [ ? [ filter ] [ ? extensions ] ] ] ] ] + * + * where: + * hostport is a host or a host:port list that can be space-separated. + * attributes is a comma separated list + * scope is one of these three strings: base one sub (default=base) + * filter is a string-represented filter as in RFC 2254 + * extensions is a comma-separated list of name=value pairs. + * + * e.g., ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich + * + * To accomodate IPv6 addresses, the host portion of a host that appears + * in hostport can be enclosed in square brackets, e.g + * + * e.g., ldap://[fe80::a00:20ff:fee5:c0b4]:3389/dc=siroe,dc=com + * + * We also tolerate URLs that look like: and + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +#include "ldap-int.h" + + +static int skip_url_prefix( const char **urlp, int *enclosedp, int *securep ); + + +int +LDAP_CALL +ldap_is_ldap_url( const char *url ) +{ + int enclosed, secure; + + return( url != NULL + && skip_url_prefix( &url, &enclosed, &secure )); +} + + +static int +skip_url_prefix( const char **urlp, int *enclosedp, int *securep ) +{ +/* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + * The data that *urlp points to is not changed by this function. + */ + if ( *urlp == NULL ) { + return( 0 ); + } + + /* skip leading '<' (if any) */ + if ( **urlp == '<' ) { + *enclosedp = 1; + ++*urlp; + } else { + *enclosedp = 0; + } + + /* skip leading "URL:" (if any) */ + if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp( + *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { + *urlp += LDAP_URL_URLCOLON_LEN; + } + + /* check for an "ldap://" prefix */ + if ( strlen( *urlp ) >= LDAP_URL_PREFIX_LEN && strncasecmp( *urlp, + LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) { + /* skip over URL prefix and return success */ + *urlp += LDAP_URL_PREFIX_LEN; + *securep = 0; + return( 1 ); + } + + /* check for an "ldaps://" prefix */ + if ( strlen( *urlp ) >= LDAPS_URL_PREFIX_LEN && strncasecmp( *urlp, + LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) { + /* skip over URL prefix and return success */ + *urlp += LDAPS_URL_PREFIX_LEN; + *securep = 1; + return( 1 ); + } + + return( 0 ); /* not an LDAP URL */ +} + + +int +LDAP_CALL +ldap_url_parse_no_defaults( const char *url, LDAPURLDesc **ludpp, int dn_required) +{ + return( nsldapi_url_parse( url, ludpp, dn_required ) ); +} + + +int +LDAP_CALL +ldap_url_parse( const char *url, LDAPURLDesc **ludpp ) +{ +/* + * Pick apart the pieces of an LDAP URL. + */ + int rc; + + if (( rc = nsldapi_url_parse( url, ludpp, 1 )) == 0 ) { + if ( (*ludpp)->lud_scope == -1 ) { + (*ludpp)->lud_scope = LDAP_SCOPE_BASE; + } + if ( (*ludpp)->lud_filter == NULL ) { + (*ludpp)->lud_filter = "(objectclass=*)"; + } + if ( *((*ludpp)->lud_dn) == '\0' ) { + (*ludpp)->lud_dn = NULL; + } + } else if ( rc == LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION ) { + rc = LDAP_URL_ERR_PARAM; /* mapped for backwards compatibility */ + } + + return( rc ); +} + + +/* + * like ldap_url_parse() with a few exceptions: + * 1) if dn_required is zero, a missing DN does not generate an error + * (we just leave the lud_dn field NULL) + * 2) no defaults are set for lud_scope and lud_filter (they are set to -1 + * and NULL respectively if no SCOPE or FILTER are present in the URL). + * 3) when there is a zero-length DN in a URL we do not set lud_dn to NULL. + * + * note that LDAPv3 URL extensions are ignored unless they are marked + * critical, in which case an LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION error + * is returned. + */ +int +nsldapi_url_parse( const char *url, LDAPURLDesc **ludpp, int dn_required ) +{ + + LDAPURLDesc *ludp; + char *urlcopy, *attrs, *scope, *extensions = NULL, *p, *q; + int enclosed, secure, i, nattrs, at_start; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_url_parse(%s)\n", url, 0, 0 ); + + if ( url == NULL || ludpp == NULL ) { + return( LDAP_URL_ERR_PARAM ); + } + + *ludpp = NULL; /* pessimistic */ + + if ( !skip_url_prefix( &url, &enclosed, &secure )) { + return( LDAP_URL_ERR_NOTLDAP ); + } + + /* allocate return struct */ + if (( ludp = (LDAPURLDesc *)NSLDAPI_CALLOC( 1, sizeof( LDAPURLDesc ))) + == NULLLDAPURLDESC ) { + return( LDAP_URL_ERR_MEM ); + } + + if ( secure ) { + ludp->lud_options |= LDAP_URL_OPT_SECURE; + } + + /* make working copy of the remainder of the URL */ + if (( urlcopy = nsldapi_strdup( url )) == NULL ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_MEM ); + } + + if ( enclosed && *((p = urlcopy + strlen( urlcopy ) - 1)) == '>' ) { + *p = '\0'; + } + + /* initialize scope and filter */ + ludp->lud_scope = -1; + ludp->lud_filter = NULL; + + /* lud_string is the only malloc'd string space we use */ + ludp->lud_string = urlcopy; + + /* scan forward for '/' that marks end of hostport and begin. of dn */ + if (( ludp->lud_dn = strchr( urlcopy, '/' )) == NULL ) { + if ( dn_required ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_NODN ); + } + } else { + /* terminate hostport; point to start of dn */ + *ludp->lud_dn++ = '\0'; + } + + + if ( *urlcopy == '\0' ) { + ludp->lud_host = NULL; + } else { + ludp->lud_host = urlcopy; + nsldapi_hex_unescape( ludp->lud_host ); + + /* + * Locate and strip off optional port number (:#) in host + * portion of URL. + * + * If more than one space-separated host is listed, we only + * look for a port number within the right-most one since + * ldap_init() will handle host parameters that look like + * host:port anyway. + */ + if (( p = strrchr( ludp->lud_host, ' ' )) == NULL ) { + p = ludp->lud_host; + } else { + ++p; + } + if ( *p == '[' && ( q = strchr( p, ']' )) != NULL ) { + /* square brackets present -- skip past them */ + p = q++; + } + if (( p = strchr( p, ':' )) != NULL ) { + *p++ = '\0'; + ludp->lud_port = atoi( p ); + if ( *ludp->lud_host == '\0' ) { + ludp->lud_host = NULL; /* no hostname */ + } + } + } + + /* scan for '?' that marks end of dn and beginning of attributes */ + attrs = NULL; + if ( ludp->lud_dn != NULL && + ( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) { + /* terminate dn; point to start of attrs. */ + *attrs++ = '\0'; + + /* scan for '?' that marks end of attrs and begin. of scope */ + if (( p = strchr( attrs, '?' )) != NULL ) { + /* + * terminate attrs; point to start of scope and scan for + * '?' that marks end of scope and begin. of filter + */ + *p++ = '\0'; + scope = p; + + if (( p = strchr( scope, '?' )) != NULL ) { + /* terminate scope; point to start of filter */ + *p++ = '\0'; + if ( *p != '\0' ) { + ludp->lud_filter = p; + /* + * scan for the '?' that marks the end + * of the filter and the start of any + * extensions + */ + if (( p = strchr( ludp->lud_filter, '?' )) + != NULL ) { + *p++ = '\0'; /* term. filter */ + extensions = p; + } + if ( *ludp->lud_filter == '\0' ) { + ludp->lud_filter = NULL; + } else { + nsldapi_hex_unescape( ludp->lud_filter ); + } + } + } + + if ( strcasecmp( scope, "one" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_ONELEVEL; + } else if ( strcasecmp( scope, "base" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_BASE; + } else if ( strcasecmp( scope, "sub" ) == 0 ) { + ludp->lud_scope = LDAP_SCOPE_SUBTREE; + } else if ( *scope != '\0' ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_BADSCOPE ); + } + } + } + + if ( ludp->lud_dn != NULL ) { + nsldapi_hex_unescape( ludp->lud_dn ); + } + + /* + * if attrs list was included, turn it into a null-terminated array + */ + if ( attrs != NULL && *attrs != '\0' ) { + nsldapi_hex_unescape( attrs ); + for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) { + if ( *p == ',' ) { + ++nattrs; + } + } + + if (( ludp->lud_attrs = (char **)NSLDAPI_CALLOC( nattrs + 1, + sizeof( char * ))) == NULL ) { + ldap_free_urldesc( ludp ); + return( LDAP_URL_ERR_MEM ); + } + + for ( i = 0, p = attrs; i < nattrs; ++i ) { + ludp->lud_attrs[ i ] = p; + if (( p = strchr( p, ',' )) != NULL ) { + *p++ ='\0'; + } + nsldapi_hex_unescape( ludp->lud_attrs[ i ] ); + } + } + + /* if extensions list was included, check for critical ones */ + if ( extensions != NULL && *extensions != '\0' ) { + /* Note: at present, we do not recognize ANY extensions */ + at_start = 1; + for ( p = extensions; *p != '\0'; ++p ) { + if ( at_start ) { + if ( *p == '!' ) { /* critical extension */ + ldap_free_urldesc( ludp ); + return( LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION ); + } + at_start = 0; + } else if ( *p == ',' ) { + at_start = 1; + } + } + } + + *ludpp = ludp; + + return( 0 ); +} + + +void +LDAP_CALL +ldap_free_urldesc( LDAPURLDesc *ludp ) +{ + if ( ludp != NULLLDAPURLDESC ) { + if ( ludp->lud_string != NULL ) { + NSLDAPI_FREE( ludp->lud_string ); + } + if ( ludp->lud_attrs != NULL ) { + NSLDAPI_FREE( ludp->lud_attrs ); + } + NSLDAPI_FREE( ludp ); + } +} + + +int +LDAP_CALL +ldap_url_search( LDAP *ld, const char *url, int attrsonly ) +{ + int err, msgid; + LDAPURLDesc *ludp; + BerElement *ber; + LDAPServer *srv; + char *host; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( -1 ); /* punt */ + } + + if ( ldap_url_parse( url, &ludp ) != 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( -1 ); + } + + LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK ); + msgid = ++ld->ld_msgid; + LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK ); + + if ( nsldapi_build_search_req( ld, ludp->lud_dn, ludp->lud_scope, + ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL, + -1, -1, msgid, &ber ) != LDAP_SUCCESS ) { + return( -1 ); + } + + err = 0; + + if ( ludp->lud_host == NULL ) { + host = ld->ld_defhost; + } else { + host = ludp->lud_host; + } + + if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer ))) + == NULL || ( host != NULL && + ( srv->lsrv_host = nsldapi_strdup( host )) == NULL )) { + if ( srv != NULL ) { + NSLDAPI_FREE( srv ); + } + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + err = -1; + } else { + if ( ludp->lud_port != 0 ) { + /* URL includes a port - use it */ + srv->lsrv_port = ludp->lud_port; + } else if ( ludp->lud_host == NULL ) { + /* URL has no port or host - use port from ld */ + srv->lsrv_port = ld->ld_defport; + } else if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) == 0 ) { + /* ldap URL has a host but no port - use std. port */ + srv->lsrv_port = LDAP_PORT; + } else { + /* ldaps URL has a host but no port - use std. port */ + srv->lsrv_port = LDAPS_PORT; + } + } + + if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 ) { + srv->lsrv_options |= LDAP_SRV_OPT_SECURE; + } + + if ( err != 0 ) { + ber_free( ber, 1 ); + } else { + err = nsldapi_send_server_request( ld, ber, msgid, NULL, srv, + NULL, NULL, 1 ); + } + + ldap_free_urldesc( ludp ); + return( err ); +} + + +int +LDAP_CALL +ldap_url_search_st( LDAP *ld, const char *url, int attrsonly, + struct timeval *timeout, LDAPMessage **res ) +{ + int msgid; + + /* + * It is an error to pass in a zero'd timeval. + */ + if ( timeout != NULL && timeout->tv_sec == 0 && + timeout->tv_usec == 0 ) { + if ( ld != NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + } + if ( res != NULL ) { + *res = NULL; + } + return( LDAP_PARAM_ERROR ); + } + + if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) { + (void) ldap_abandon( ld, msgid ); + LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL ); + return( LDAP_TIMEOUT ); + } + + return( ldap_result2error( ld, *res, 0 )); +} + + +int +LDAP_CALL +ldap_url_search_s( LDAP *ld, const char *url, int attrsonly, LDAPMessage **res ) +{ + int msgid; + + if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + return( ldap_result2error( ld, *res, 0 )); +} diff --git a/ldap/c-sdk/libraries/libldap/userstatusctrl.c b/ldap/c-sdk/libraries/libldap/userstatusctrl.c new file mode 100644 index 0000000000..1127d67bb7 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/userstatusctrl.c @@ -0,0 +1,228 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): abobrov@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_create_userstatus_control: + + Parameters are + + ld LDAP pointer to the desired connection + + ctl_iscritical Indicates whether the control is critical of not. If + this field is non-zero, the operation will only be car- + ried out if the control is recognized by the server + and/or client + + ctrlp the address of a place to put the constructed control +*/ + +int +LDAP_CALL +ldap_create_userstatus_control ( + LDAP *ld, + const char ctl_iscritical, + LDAPControl **ctrlp + ) +{ + int rc; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + rc = nsldapi_build_control( LDAP_CONTROL_ACCOUNT_USABLE, + NULL, 0, ctl_iscritical, ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + +/* ldap_parse_userstatus_control: + + Parameters are + + ld LDAP pointer to the desired connection + + ctrlp An array of controls obtained from calling + ldap_parse_result on the set of results + returned by the server + + us the address of struct LDAPuserstatus + to parse control results to +*/ + +int +LDAP_CALL +ldap_parse_userstatus_control ( + LDAP *ld, + LDAPControl **ctrlp, + LDAPuserstatus *us + ) +{ + BerElement *ber = NULL; + int i, foundUSControl; + LDAPControl *USCtrlp = NULL; + ber_tag_t tag; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || us == NULL ) { + return( LDAP_PARAM_ERROR ); + } + + /* find the control in the list of controls if it exists */ + if ( ctrlp == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + foundUSControl = 0; + for ( i = 0; (( ctrlp[i] != NULL ) && ( !foundUSControl )); i++ ) { + foundUSControl = !strcmp( ctrlp[i]->ldctl_oid, LDAP_CONTROL_ACCOUNT_USABLE ); + } + if ( !foundUSControl ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } else { + /* let local var point to the control */ + USCtrlp = ctrlp[i-1]; + } + + /* allocate a Ber element with the contents of the control's struct berval */ + if ( ( ber = ber_init( &USCtrlp->ldctl_value ) ) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + memset( us, 0, sizeof(struct LDAPuserstatus) ); + + /* + * The control value should look like this: + * + * ACCOUNT_USABLE_RESPONSE::= CHOICE { + * is_available [0] INTEGER, ** seconds before expiration ** + * is_not_available [1] More_info + * } + * More_info::= SEQUENCE { + * inactive [0] BOOLEAN DEFAULT FALSE, + * reset [1] BOOLEAN DEFAULT FALSE, + * expired [2] BOOLEAN DEFAULT FALSE, + * remaining_grace [3] INTEGER OPTIONAL, + * seconds_before_unlock [4] INTEGER OPTIONAL + * } + */ + + if ( ( ber_scanf( ber, "t", &tag ) ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + + tag = (( tag & LBER_CONSTRUCTED ) == LBER_CONSTRUCTED ) ? 1 : 0; + + if ( !tag ) { + us->us_available = 1; + if ( ber_scanf( ber, "i", &us->us_expire ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + } else { + us->us_available = 0; + tag = 0; + if ( ( ber_scanf( ber, "{t", &tag ) ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + while ( tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET ) { + tag = tag & (~LBER_CLASS_CONTEXT); + switch (tag) + { + case 0: + if ( ber_scanf( ber, "b", &us->us_inactive ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + us->us_inactive = ( us->us_inactive != 0 ) ? 1 : 0; + break; + case 1: + if ( ber_scanf( ber, "b", &us->us_reset ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + us->us_reset = ( us->us_reset != 0 ) ? 1 : 0; + break; + case 2: + if ( ber_scanf( ber, "b", &us->us_expired ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + us->us_expired = ( us->us_expired != 0 ) ? 1 : 0; + break; + case 3: + if ( ber_scanf( ber, "i", &us->us_remaining ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + break; + case 4: + if ( ber_scanf( ber, "i", &us->us_seconds ) == LBER_ERROR ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + break; + default: + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + ber_scanf( ber, "t", &tag ); + } + } + + /* the ber encoding is no longer needed */ + ber_free( ber, 1 ); + return( LDAP_SUCCESS ); +} diff --git a/ldap/c-sdk/libraries/libldap/utf8.c b/ldap/c-sdk/libraries/libldap/utf8.c new file mode 100644 index 0000000000..98957cc5b4 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/utf8.c @@ -0,0 +1,282 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* uft8.c - misc. utf8 "string" functions. */ +#include "ldap-int.h" + +static char UTF8len[64] += {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6}; + +int +LDAP_CALL +ldap_utf8len (const char* s) + /* Return the number of char's in the character at *s. */ +{ + return ldap_utf8next((char*)s) - s; +} + +char* +LDAP_CALL +ldap_utf8next (char* s) + /* Return a pointer to the character immediately following *s. + Handle any valid UTF-8 character, including '\0' and ASCII. + Try to handle a misaligned pointer or a malformed character. + */ +{ + register unsigned char* next = (unsigned char*)s; + switch (UTF8len [(*next >> 2) & 0x3F]) { + case 0: /* erroneous: s points to the middle of a character. */ + case 6: if ((*++next & 0xC0) != 0x80) break; + case 5: if ((*++next & 0xC0) != 0x80) break; + case 4: if ((*++next & 0xC0) != 0x80) break; + case 3: if ((*++next & 0xC0) != 0x80) break; + case 2: if ((*++next & 0xC0) != 0x80) break; + case 1: ++next; + } + return (char*) next; +} + +char* +LDAP_CALL +ldap_utf8prev (char* s) + /* Return a pointer to the character immediately preceding *s. + Handle any valid UTF-8 character, including '\0' and ASCII. + Try to handle a misaligned pointer or a malformed character. + */ +{ + register unsigned char* prev = (unsigned char*)s; + unsigned char* limit = prev - 6; + while (((*--prev & 0xC0) == 0x80) && (prev != limit)) { + ; + } + return (char*) prev; +} + +int +LDAP_CALL +ldap_utf8copy (char* dst, const char* src) + /* Copy a character from src to dst; return the number of char's copied. + Handle any valid UTF-8 character, including '\0' and ASCII. + Try to handle a misaligned pointer or a malformed character. + */ +{ + register const unsigned char* s = (const unsigned char*)src; + switch (UTF8len [(*s >> 2) & 0x3F]) { + case 0: /* erroneous: s points to the middle of a character. */ + case 6: *dst++ = *s++; if ((*s & 0xC0) != 0x80) break; + case 5: *dst++ = *s++; if ((*s & 0xC0) != 0x80) break; + case 4: *dst++ = *s++; if ((*s & 0xC0) != 0x80) break; + case 3: *dst++ = *s++; if ((*s & 0xC0) != 0x80) break; + case 2: *dst++ = *s++; if ((*s & 0xC0) != 0x80) break; + case 1: *dst = *s++; + } + return s - (const unsigned char*)src; +} + +size_t +LDAP_CALL +ldap_utf8characters (const char* src) + /* Return the number of UTF-8 characters in the 0-terminated array s. */ +{ + register char* s = (char*)src; + size_t n; + for (n = 0; *s; LDAP_UTF8INC(s)) ++n; + return n; +} + +unsigned long LDAP_CALL +ldap_utf8getcc( const char** src ) +{ + register unsigned long c = 0; + register const unsigned char* s = (const unsigned char*)*src; + switch (UTF8len [(*s >> 2) & 0x3F]) { + case 0: /* erroneous: s points to the middle of a character. */ + c = (*s++) & 0x3F; goto more5; + case 1: c = (*s++); break; + case 2: c = (*s++) & 0x1F; goto more1; + case 3: c = (*s++) & 0x0F; goto more2; + case 4: c = (*s++) & 0x07; goto more3; + case 5: c = (*s++) & 0x03; goto more4; + case 6: c = (*s++) & 0x01; goto more5; + more5: if ((*s & 0xC0) != 0x80) break; c = (c << 6) | ((*s++) & 0x3F); + more4: if ((*s & 0xC0) != 0x80) break; c = (c << 6) | ((*s++) & 0x3F); + more3: if ((*s & 0xC0) != 0x80) break; c = (c << 6) | ((*s++) & 0x3F); + more2: if ((*s & 0xC0) != 0x80) break; c = (c << 6) | ((*s++) & 0x3F); + more1: if ((*s & 0xC0) != 0x80) break; c = (c << 6) | ((*s++) & 0x3F); + break; + } + *src = (const char*)s; + return c; +} + +char* +LDAP_CALL +ldap_utf8strtok_r( char* sp, const char* brk, char** next) +{ + const char *bp; + unsigned long sc, bc; + char *tok; + + if (sp == NULL && (sp = *next) == NULL) + return NULL; + + /* Skip leading delimiters; roughly, sp += strspn(sp, brk) */ + cont: + sc = LDAP_UTF8GETC(sp); + for (bp = brk; (bc = LDAP_UTF8GETCC(bp)) != 0;) { + if (sc == bc) + goto cont; + } + + if (sc == 0) { /* no non-delimiter characters */ + *next = NULL; + return NULL; + } + tok = LDAP_UTF8PREV(sp); + + /* Scan token; roughly, sp += strcspn(sp, brk) + * Note that brk must be 0-terminated; we stop if we see that, too. + */ + while (1) { + sc = LDAP_UTF8GETC(sp); + bp = brk; + do { + if ((bc = LDAP_UTF8GETCC(bp)) == sc) { + if (sc == 0) { + *next = NULL; + } else { + *next = sp; + *(LDAP_UTF8PREV(sp)) = 0; + } + return tok; + } + } while (bc != 0); + } + /* NOTREACHED */ +} + +int +LDAP_CALL +ldap_utf8isalnum( char* s ) +{ + register unsigned char c = *(unsigned char*)s; + if (0x80 & c) return 0; + if (c >= 'A' && c <= 'Z') return 1; + if (c >= 'a' && c <= 'z') return 1; + if (c >= '0' && c <= '9') return 1; + return 0; +} + +int +LDAP_CALL +ldap_utf8isalpha( char* s ) +{ + register unsigned char c = *(unsigned char*)s; + if (0x80 & c) return 0; + if (c >= 'A' && c <= 'Z') return 1; + if (c >= 'a' && c <= 'z') return 1; + return 0; +} + +int +LDAP_CALL +ldap_utf8isdigit( char* s ) +{ + register unsigned char c = *(unsigned char*)s; + if (0x80 & c) return 0; + if (c >= '0' && c <= '9') return 1; + return 0; +} + +int +LDAP_CALL +ldap_utf8isxdigit( char* s ) +{ + register unsigned char c = *(unsigned char*)s; + if (0x80 & c) return 0; + if (c >= '0' && c <= '9') return 1; + if (c >= 'A' && c <= 'F') return 1; + if (c >= 'a' && c <= 'f') return 1; + return 0; +} + +int +LDAP_CALL +ldap_utf8isspace( char* s ) +{ + register unsigned char *c = (unsigned char*)s; + int len = ldap_utf8len(s); + + if (len == 0) { + return 0; + } else if (len == 1) { + switch (*c) { + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x20: + return 1; + default: + return 0; + } + } else if (len == 2) { + if (*c == 0xc2) { + return *(c+1) == 0x80; + } + } else if (len == 3) { + if (*c == 0xE2) { + c++; + if (*c == 0x80) { + c++; + return (*c>=0x80 && *c<=0x8a); + } + } else if (*c == 0xE3) { + return (*(c+1)==0x80) && (*(c+2)==0x80); + } else if (*c==0xEF) { + return (*(c+1)==0xBB) && (*(c+2)==0xBF); + } + return 0; + } + + /* should never reach here */ + return 0; +} diff --git a/ldap/c-sdk/libraries/libldap/vlistctrl.c b/ldap/c-sdk/libraries/libldap/vlistctrl.c new file mode 100644 index 0000000000..e42d746412 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/vlistctrl.c @@ -0,0 +1,259 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* vlistctrl.c - virtual list control implementation. */ +#include "ldap-int.h" + + + +/* + * function to create a VirtualListViewRequest control that can be passed + * to ldap_search_ext() or ldap_search_ext_s(). *ctrlp will be set to a + * freshly allocated LDAPControl structure. Returns an LDAP error code + * (LDAP_SUCCESS if all goes well). + * + * Parameters + * ld LDAP pointer to the desired connection + * + * ldvlistp the control structure. + * + * ctrlp the address of a place to put the constructed control + + The controlValue is an OCTET STRING + whose value is the BER-encoding of the following SEQUENCE: + + VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER (0 .. maxInt), + afterCount INTEGER (0 .. maxInt), + CHOICE { + byIndex [0] SEQUENCE { + index INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt) } + byValue [1] greaterThanOrEqual assertionValue } + + beforeCount indicates how many entries before the target entry the + client wants the server to send. afterCount indicates the number of + entries after the target entry the client wants the server to send. + index and contentCount identify the target entry + greaterThanOrEqual is an attribute assertion value defined in + [LDAPv3]. If present, the value supplied in greaterThanOrEqual is used + to determine the target entry by comparison with the values of the + attribute specified as the primary sort key. The first list entry who's + value is no less than the supplied value is the target entry. + + */ + +int +LDAP_CALL +ldap_create_virtuallist_control( + LDAP *ld, + LDAPVirtualList *ldvlistp, + LDAPControl **ctrlp +) +{ + BerElement *ber; + int rc; + + if (!NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + + if ( NULL == ctrlp || NULL == ldvlistp ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return ( LDAP_PARAM_ERROR ); + } + + /* create a ber package to hold the controlValue */ + if ( LDAP_SUCCESS != nsldapi_alloc_ber_with_options( ld, &ber ) ) + { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + if ( LBER_ERROR == ber_printf( ber, + "{ii", + ldvlistp->ldvlist_before_count, + ldvlistp->ldvlist_after_count )) + /* XXX lossy casts */ + { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + + if (NULL == ldvlistp->ldvlist_attrvalue) + { + if ( LBER_ERROR == ber_printf( ber, + "t{ii}}", + LDAP_TAG_VLV_BY_INDEX, + ldvlistp->ldvlist_index, + ldvlistp->ldvlist_size ) ) + /* XXX lossy casts */ + { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + else + { + if ( LBER_ERROR == ber_printf( ber, + "to}", + LDAP_TAG_VLV_BY_VALUE, + ldvlistp->ldvlist_attrvalue, + (int)strlen( ldvlistp->ldvlist_attrvalue )) ) { + LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_ENCODING_ERROR ); + } + } + + + rc = nsldapi_build_control( LDAP_CONTROL_VLVREQUEST , + ber, + 1, + 1, + ctrlp ); + + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); + +} + + +/* + * function to find and parse a VirtualListViewResponse control contained in + * "ctrls" *target_posp, *list_sizep, and *errcodep are set based on its + * contents. Returns an LDAP error code that indicates whether the parsing + * itself was successful (LDAP_SUCCESS if all goes well). + + The controlValue is an OCTET STRING, whose value + is the BER encoding of a value of the following SEQUENCE: + + VirtualListViewResponse ::= SEQUENCE { + targetPosition INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt), + virtualListViewResult ENUMERATED { + success (0), + operatonsError (1), + unwillingToPerform (53), + insufficientAccessRights (50), + busy (51), + timeLimitExceeded (3), + adminLimitExceeded (11), + sortControlMissing (60), + indexRangeError (61), + other (80) } } + + */ +int +LDAP_CALL +ldap_parse_virtuallist_control +( + LDAP *ld, + LDAPControl **ctrls, + ber_int_t *target_posp, + ber_int_t *list_sizep, + int *errcodep +) +{ + BerElement *ber; + int i, foundListControl; + ber_int_t errcode; + LDAPControl *listCtrlp; + ber_int_t target_pos, list_size; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + return( LDAP_PARAM_ERROR ); + } + + /* only ldapv3 or higher can do virtual lists. */ + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL ); + return( LDAP_NOT_SUPPORTED ); + } + + /* find the listControl in the list of controls if it exists */ + if ( ctrls == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } + + foundListControl = 0; + for ( i = 0; (( ctrls[i] != NULL ) && ( !foundListControl )); i++ ) { + foundListControl = !strcmp( ctrls[i]->ldctl_oid, + LDAP_CONTROL_VLVRESPONSE ); + } + if ( !foundListControl ) { + LDAP_SET_LDERRNO( ld, LDAP_CONTROL_NOT_FOUND, NULL, NULL ); + return ( LDAP_CONTROL_NOT_FOUND ); + } else { + /* let local var point to the listControl */ + listCtrlp = ctrls[i-1]; + } + + /* allocate a Ber element with the contents of the list_control's struct berval */ + if ( ( ber = ber_init( &listCtrlp->ldctl_value ) ) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* decode the result from the Berelement */ + if ( LBER_ERROR == ber_scanf( ber, "{iie}", &target_pos, &list_size, + &errcode ) ) { + LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL ); + ber_free( ber, 1 ); + return( LDAP_DECODING_ERROR ); + } + + if ( target_posp != NULL ) { + *target_posp = target_pos; + } + if ( list_sizep != NULL ) { + *list_sizep = list_size; + } + if ( errcodep != NULL ) { + *errcodep = (int)errcode; + } + + /* the ber encoding is no longer needed */ + ber_free(ber,1); + + return(LDAP_SUCCESS); + +} diff --git a/ldap/c-sdk/libraries/libldap/whoami.c b/ldap/c-sdk/libraries/libldap/whoami.c new file mode 100644 index 0000000000..35e10e71c7 --- /dev/null +++ b/ldap/c-sdk/libraries/libldap/whoami.c @@ -0,0 +1,130 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Sun LDAP C SDK. + * + * The Initial Developer of the Original Code is Sun Microsystems, Inc. + * + * Portions created by Sun Microsystems, Inc are Copyright (C) 2005 + * Sun Microsystems, Inc. All Rights Reserved. + * + * Contributor(s): abobrov@sun.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ldap-int.h" + +/* ldap_whoami */ +int +LDAP_CALL +ldap_whoami ( + LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp + ) +{ + int rc; + struct berval *requestdata = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_WHO_AM_I, requestdata, + serverctrls, clientctrls, msgidp ); + + return( rc ); +} + +/* ldap_parse_whoami */ +int +LDAP_CALL +ldap_parse_whoami ( + LDAP *ld, + LDAPMessage *result, + struct berval **authzid + ) +{ + int rc; + char *retoidp = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + if ( !result ) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + *authzid = NULL; + + rc = ldap_parse_extended_result( ld, result, &retoidp, authzid, 0 ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + ldap_memfree( retoidp ); + return( LDAP_SUCCESS ); +} + +/* ldap_whoami_s */ +int +LDAP_CALL +ldap_whoami_s ( + LDAP *ld, + struct berval **authzid, + LDAPControl **serverctrls, + LDAPControl **clientctrls + ) +{ + int rc; + int msgid; + LDAPMessage *result = NULL; + + if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { + LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + rc = ldap_whoami( ld, serverctrls, clientctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ); + if ( rc == -1 ) { + return( LDAP_GET_LDERRNO( ld, NULL, NULL ) ); + } + + rc = ldap_parse_whoami( ld, result, authzid ); + + ldap_msgfree( result ); + + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libldif/libldif.def b/ldap/c-sdk/libraries/libldif/libldif.def new file mode 100644 index 0000000000..1c5ad53820 --- /dev/null +++ b/ldap/c-sdk/libraries/libldif/libldif.def @@ -0,0 +1,58 @@ +; +; ***** BEGIN LICENSE BLOCK ***** +; Version: MPL 1.1/GPL 2.0/LGPL 2.1 +; +; The contents of this file are subject to the Mozilla Public License Version +; 1.1 (the "License"); you may not use this file except in compliance with +; the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" basis, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +; for the specific language governing rights and limitations under the +; License. +; +; The Original Code is Mozilla Communicator client code. +; +; The Initial Developer of the Original Code is +; Netscape Communications Corporation. +; Portions created by the Initial Developer are Copyright (C) 1996-1999 +; the Initial Developer. All Rights Reserved. +; +; Contributor(s): +; +; Alternatively, the contents of this file may be used under the terms of +; either of the GNU General Public License Version 2 or later (the "GPL"), +; or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +; in which case the provisions of the GPL or the LGPL are applicable instead +; of those above. If you wish to allow use of your version of this file only +; under the terms of either the GPL or the LGPL, and not to allow others to +; use your version of this file under the terms of the MPL, indicate your +; decision by deleting the provisions above and replace them with the notice +; and other provisions required by the GPL or the LGPL. If you do not delete +; the provisions above, a recipient may use your version of this file under +; the terms of any one of the MPL, the GPL or the LGPL. +; +; ***** END LICENSE BLOCK ***** + +LIBRARY LDIF60 +VERSION 6.0 +HEAPSIZE 4096 + +EXPORTS +; exports list (generated by genexports.pl) +; + ldif_parse_line + ldif_getline + ldif_put_type_and_value + ldif_put_type_and_value_nowrap + ldif_put_type_and_value_with_options + ldif_type_and_value + ldif_type_and_value_nowrap + ldif_type_and_value_with_options + ldif_base64_decode + ldif_base64_encode + ldif_base64_encode_nowrap + ldif_get_entry +; +; end of generated exports list. diff --git a/ldap/c-sdk/libraries/libldif/line64.c b/ldap/c-sdk/libraries/libldif/line64.c new file mode 100644 index 0000000000..a819825cce --- /dev/null +++ b/ldap/c-sdk/libraries/libldif/line64.c @@ -0,0 +1,612 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* line64.c - routines for dealing with the slapd line format */ + +#include +#include +#include +#include +#ifndef macintosh +#include +#endif +#ifdef _WIN32 +#include +#elif !defined( macintosh ) +#include +#endif +#include "ldaplog.h" +#include "ldif.h" + +#ifndef isascii +#define isascii( c ) (!((c) & ~0177)) +#endif + +#define RIGHT2 0x03 +#define RIGHT4 0x0f +#define CONTINUED_LINE_MARKER '\001' + +#define ISBLANK(c) (c == ' ' || c == '\t' || c == '\n') /* not "\r\v\f" */ + +#define LDIF_OPT_ISSET( value, opt ) (((value) & (opt)) != 0 ) + +static char nib2b64[0x40f] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static unsigned char b642nib[0x80] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static int ldif_base64_encode_internal( unsigned char *src, char *dst, int srclen, + int lenused, int wraplen ); + +/* + * ldif_parse_line - takes a line of the form "type:[:] value" and splits it + * into components "type" and "value". if a double colon separates type from + * value, then value is encoded in base 64, and parse_line un-decodes it + * (in place) before returning. + */ + +int +ldif_parse_line( + char *line, + char **type, + char **value, + int *vlen +) +{ + char *p, *s, *d; + int b64; + + /* skip any leading space */ + while ( ISBLANK( *line ) ) { + line++; + } + *type = line; + + for ( s = line; *s && *s != ':'; s++ ) + ; /* NULL */ + if ( *s == '\0' ) { + + /* Comment-out while we address calling libldif from ns-back-ldbm + on NT. 1 of 3 */ +#if defined( _WIN32 ) + /* +#endif + LDAPDebug( LDAP_DEBUG_PARSE, "ldif_parse_line: missing ':' " + "on line \"%s\"\n", line, 0, 0 ); +#if defined( _WIN32 ) + */ +#endif + return( -1 ); + } + + /* trim any space between type and : */ + for ( p = s - 1; p > line && ISBLANK( *p ); p-- ) { + *p = '\0'; + } + *s++ = '\0'; + + /* check for double : - indicates base 64 encoded value */ + if ( *s == ':' ) { + s++; + b64 = 1; + + /* single : - normally encoded value */ + } else { + b64 = 0; + } + + /* skip space between : and value */ + while ( ISBLANK( *s ) ) { + s++; + } + + /* + * If no value is present, return a zero-length string for + * *value, with *vlen set to zero. + */ + if ( *s == '\0' ) { + *value = s; + *vlen = 0; + return( 0 ); + } + + /* check for continued line markers that should be deleted */ + for ( p = s, d = s; *p; p++ ) { + if ( *p != CONTINUED_LINE_MARKER ) + *d++ = *p; + } + *d = '\0'; + + *value = s; + if ( b64 ) { + if (( *vlen = ldif_base64_decode( s, (unsigned char *)s )) + < 0 ) { + /* Comment-out while we address calling libldif from ns-back-ldbm + on NT. 3 of 3 */ +#if defined( _WIN32 ) + /* +#endif + LDAPDebug( LDAP_DEBUG_ANY, + "ldif_parse_line: invalid base 64 char on line \"%s\"\n", + line, 0, 0 ); +#if defined( _WIN32 ) + */ +#endif + return( -1 ); + } + s[ *vlen ] = '\0'; + } else { + *vlen = (int) (d - s); + } + + return( 0 ); +} + + +/* + * ldif_base64_decode - take the BASE64-encoded characters in "src" + * (a zero-terminated string) and decode them into the the buffer "dst". + * "src" and "dst" can be the same if in-place decoding is desired. + * "dst" must be large enough to hold the decoded octets. No more than + * 3 * strlen( src ) / 4 bytes will be produced. + * "dst" may contain zero octets anywhere within it, but it is not + * zero-terminated by this function. + * + * The number of bytes copied to "dst" is returned if all goes well. + * -1 is returned if the BASE64 encoding in "src" is invalid. + */ + +int +ldif_base64_decode( char *src, unsigned char *dst ) +{ + char *p, *stop; + unsigned char nib, *byte; + int i, len; + + stop = strchr( src, '\0' ); + byte = dst; + for ( p = src, len = 0; p < stop; p += 4, len += 3 ) { + for ( i = 0; i < 4; i++ ) { + if ( p[i] != '=' && (p[i] & 0x80 || + b642nib[ p[i] & 0x7f ] > 0x3f) ) { + return( -1 ); + } + } + + /* first digit */ + nib = b642nib[ p[0] & 0x7f ]; + byte[0] = nib << 2; + + /* second digit */ + nib = b642nib[ p[1] & 0x7f ]; + byte[0] |= nib >> 4; + + /* third digit */ + if ( p[2] == '=' ) { + len += 1; + break; + } + byte[1] = (nib & RIGHT4) << 4; + nib = b642nib[ p[2] & 0x7f ]; + byte[1] |= nib >> 2; + + /* fourth digit */ + if ( p[3] == '=' ) { + len += 2; + break; + } + byte[2] = (nib & RIGHT2) << 6; + nib = b642nib[ p[3] & 0x7f ]; + byte[2] |= nib; + + byte += 3; + } + + return( len ); +} + +/* + * ldif_getline - return the next "line" (minus newline) of input from a + * string buffer of lines separated by newlines, terminated by \n\n + * or \0. this routine handles continued lines, bundling them into + * a single big line before returning. if a line begins with a white + * space character, it is a continuation of the previous line. the white + * space character (nb: only one char), and preceeding newline are changed + * into CONTINUED_LINE_MARKER chars, to be deleted later by the + * ldif_parse_line() routine above. + * + * it takes a pointer to a pointer to the buffer on the first call, + * which it updates and must be supplied on subsequent calls. + * + * XXX need to update this function to also support as EOL. + * XXX supports as of 07/29/1998 (richm) + */ + +char * +ldif_getline( char **next ) +{ + char *l; + char c; + char *p; + + if ( *next == NULL || **next == '\n' || **next == '\0' ) { + return( NULL ); + } + + while ( **next == '#' ) { /* skip comment lines */ + if (( *next = strchr( *next, '\n' )) == NULL ) { + return( NULL ); + } + (*next)++; + } + + l = *next; + while ( (*next = strchr( *next, '\n' )) != NULL ) { + p = *next - 1; /* pointer to character previous to the newline */ + c = *(*next + 1); /* character after the newline */ + if ( ISBLANK( c ) && c != '\n' ) { + /* DOS EOL is \r\n, so if the character before */ + /* the \n is \r, continue it too */ + if (*p == '\r') + *p = CONTINUED_LINE_MARKER; + **next = CONTINUED_LINE_MARKER; + *(*next+1) = CONTINUED_LINE_MARKER; + } else { + /* DOS EOL is \r\n, so if the character before */ + /* the \n is \r, null it too */ + if (*p == '\r') + *p = '\0'; + *(*next)++ = '\0'; + break; + } + (*next)++; + } + + return( l ); +} + + +#define LDIF_SAFE_CHAR( c ) ( (c) != '\r' && (c) != '\n' ) +#define LDIF_CONSERVATIVE_CHAR( c ) ( LDIF_SAFE_CHAR(c) && isascii((c)) \ + && ( isprint((c)) || (c) == '\t' )) +#define LDIF_SAFE_INITCHAR( c ) ( LDIF_SAFE_CHAR(c) && (c) != ':' \ + && (c) != ' ' && (c) != '<' ) +#define LDIF_CONSERVATIVE_INITCHAR( c ) ( LDIF_SAFE_INITCHAR( c ) && \ + ! ( isascii((c)) && isspace((c)))) +#define LDIF_CONSERVATIVE_FINALCHAR( c ) ( (c) != ' ' ) + + +void +ldif_put_type_and_value_with_options( char **out, char *t, char *val, + int vlen, unsigned long options ) +{ + unsigned char *p, *byte, *stop; + char *save; + int b64, len, savelen, wraplen; + len = 0; + + if ( LDIF_OPT_ISSET( options, LDIF_OPT_NOWRAP )) { + wraplen = -1; + } else { + wraplen = LDIF_MAX_LINE_WIDTH; + } + + /* put the type + ": " */ + for ( p = (unsigned char *) t; *p; p++, len++ ) { + *(*out)++ = *p; + } + *(*out)++ = ':'; + len++; + if ( LDIF_OPT_ISSET( options, LDIF_OPT_VALUE_IS_URL )) { + *(*out)++ = '<'; /* add '<' for URLs */ + len++; + } + save = *out; + savelen = len; + b64 = 0; + + stop = (unsigned char *)val; + if ( val && vlen > 0 ) { + *(*out)++ = ' '; + stop = (unsigned char *) (val + vlen); + if ( LDIF_OPT_ISSET( options, LDIF_OPT_MINIMAL_ENCODING )) { + if ( !LDIF_SAFE_INITCHAR( val[0] )) { + b64 = 1; + } + } else { + if ( !LDIF_CONSERVATIVE_INITCHAR( val[0] ) || + !LDIF_CONSERVATIVE_FINALCHAR( val[vlen-1] )) { + b64 = 1; + } + } + } + + if ( !b64 ) { + for ( byte = (unsigned char *) val; byte < stop; + byte++, len++ ) { + if ( LDIF_OPT_ISSET( options, + LDIF_OPT_MINIMAL_ENCODING )) { + if ( !LDIF_SAFE_CHAR( *byte )) { + b64 = 1; + break; + } + } else if ( !LDIF_CONSERVATIVE_CHAR( *byte )) { + b64 = 1; + break; + } + + if ( wraplen != -1 && len > wraplen ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + *(*out)++ = *byte; + } + } + + if ( b64 ) { + *out = save; + *(*out)++ = ':'; + *(*out)++ = ' '; + len = ldif_base64_encode_internal( (unsigned char *)val, *out, vlen, + savelen + 2, wraplen ); + *out += len; + } + + *(*out)++ = '\n'; +} + +void +ldif_put_type_and_value( char **out, char *t, char *val, int vlen ) +{ + ldif_put_type_and_value_with_options( out, t, val, vlen, 0 ); +} + +void +ldif_put_type_and_value_nowrap( char **out, char *t, char *val, int vlen ) +{ + ldif_put_type_and_value_with_options( out, t, val, vlen, LDIF_OPT_NOWRAP ); +} + +/* + * ldif_base64_encode_internal - encode "srclen" bytes in "src", place BASE64 + * encoded bytes in "dst" and return the length of the BASE64 + * encoded string. "dst" is also zero-terminated by this function. + * + * If "lenused" >= 0, newlines will be included in "dst" and "lenused" if + * appropriate. "lenused" should be a count of characters already used + * on the current line. The LDIF lines we create will contain at most + * "wraplen" characters on each line, unless "wraplen" is -1, in which + * case output line length is unlimited. + * + * If "lenused" < 0, no newlines will be included, and the LDIF_BASE64_LEN() + * macro can be used to determine how many bytes will be placed in "dst." + */ + +static int +ldif_base64_encode_internal( unsigned char *src, char *dst, int srclen, int lenused, int wraplen ) +{ + unsigned char *byte, *stop; + unsigned char buf[3]; + char *out; + unsigned long bits; + int i, pad, len; + + len = 0; + out = dst; + stop = src + srclen; + + /* convert to base 64 (3 bytes => 4 base 64 digits) */ + for ( byte = src; byte < stop - 2; byte += 3 ) { + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, bits <<= 6 ) { + if ( wraplen != -1 && lenused >= 0 && lenused++ > wraplen ) { + *out++ = '\n'; + *out++ = ' '; + lenused = 2; + } + + /* get b64 digit from high order 6 bits */ + *out++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } + } + + /* add padding if necessary */ + if ( byte < stop ) { + for ( i = 0; byte + i < stop; i++ ) { + buf[i] = byte[i]; + } + for ( pad = 0; i < 3; i++, pad++ ) { + buf[i] = '\0'; + } + byte = buf; + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, bits <<= 6 ) { + if ( wraplen != -1 && lenused >= 0 && lenused++ > wraplen ) { + *out++ = '\n'; + *out++ = ' '; + lenused = 2; + } + + if (( i == 3 && pad > 0 ) || ( i == 2 && pad == 2 )) { + /* Pad as appropriate */ + *out++ = '='; + } else { + /* get b64 digit from low order 6 bits */ + *out++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } + } + } + + *out = '\0'; + + return( out - dst ); +} + +int +ldif_base64_encode( unsigned char *src, char *dst, int srclen, int lenused ) +{ + return ldif_base64_encode_internal( src, dst, srclen, lenused, LDIF_MAX_LINE_WIDTH ); +} + +int +ldif_base64_encode_nowrap( unsigned char *src, char *dst, int srclen, int lenused ) +{ + return ldif_base64_encode_internal( src, dst, srclen, lenused, -1 ); +} + + +/* + * return malloc'd, zero-terminated LDIF line + */ +char * +ldif_type_and_value_with_options( char *type, char *val, int vlen, + unsigned long options ) +{ + char *buf, *p; + int tlen; + + tlen = strlen( type ); + if (( buf = (char *)malloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 )) != + NULL ) { + p = buf; + ldif_put_type_and_value_with_options( &p, type, val, vlen, options ); + *p = '\0'; + } + + return( buf ); +} + +char * +ldif_type_and_value( char *type, char *val, int vlen ) +{ + return ldif_type_and_value_with_options( type, val, vlen, 0 ); +} + +char * +ldif_type_and_value_nowrap( char *type, char *val, int vlen ) +{ + return ldif_type_and_value_with_options( type, val, vlen, LDIF_OPT_NOWRAP ); +} + +/* + * ldif_get_entry - read the next ldif entry from the FILE referenced + * by fp. return a pointer to a malloc'd, null-terminated buffer. also + * returned is the last line number read, in *lineno. + */ +char * +ldif_get_entry( FILE *fp, int *lineno ) +{ + char line[BUFSIZ]; + char *buf; + int max, cur, len, gotsome; + + buf = NULL; + max = cur = gotsome = 0; + while ( fgets( line, sizeof(line), fp ) != NULL ) { + if ( lineno != NULL ) { + (*lineno)++; + } + /* ldif entries are terminated by a \n on a line by itself */ + if ( line[0] == '\0' || line[0] == '\n' +#if !defined( XP_WIN32 ) + || ( line[0] == '\r' && line[1] == '\n' ) /* DOS format */ +#endif + ) { + if ( gotsome ) { + break; + } else { + continue; + } + } else if ( line[0] == '#' ) { + continue; + } + gotsome = 1; + len = strlen( line ); +#if !defined( XP_WIN32 ) + /* DOS format */ + if ( len > 0 && line[len-1] == '\r' ) { + --len; + line[len] = '\0'; + } else if ( len > 1 && line[len-2] == '\r' && line[len-1] == '\n' ) { + --len; + line[len-1] = line[len]; + line[len] = '\0'; + } +#endif + while ( cur + (len + 1) > max ) { + if ( buf == NULL ) { + max += BUFSIZ; + buf = (char *) malloc( max ); + } else { + max *= 2; + buf = (char *) realloc( buf, max ); + } + if ( buf == NULL ) { + return( NULL ); + } + } + + memcpy( buf + cur, line, len + 1 ); + cur += len; + } + + return( buf ); +} diff --git a/ldap/c-sdk/libraries/libldif/moz.build b/ldap/c-sdk/libraries/libldif/moz.build new file mode 100644 index 0000000000..000775a249 --- /dev/null +++ b/ldap/c-sdk/libraries/libldif/moz.build @@ -0,0 +1,23 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include('/ldap/ldap-sdk.mozbuild') + +SharedLibrary('ldif60') + +SOURCES += [ + 'line64.c' +] + +LOCAL_INCLUDES += [ + '/ldap/c-sdk/include' +] + +if CONFIG['OS_ARCH'] == 'WINNT': + DEFFILE = SRCDIR + '/libldif.def' + +DEFINES['USE_WAITPID'] = True +DEFINES['NEEDPROTOS'] = True + diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-dns.c b/ldap/c-sdk/libraries/libprldap/ldappr-dns.c new file mode 100644 index 0000000000..a88bfe31c7 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-dns.c @@ -0,0 +1,166 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * DNS callback functions for libldap that use the NSPR (Netscape + * Portable Runtime) thread API. + * + */ + +#include "ldappr-int.h" + +static LDAPHostEnt *prldap_gethostbyname( const char *name, + LDAPHostEnt *result, char *buffer, int buflen, int *statusp, + void *extradata ); +static LDAPHostEnt *prldap_gethostbyaddr( const char *addr, int length, + int type, LDAPHostEnt *result, char *buffer, int buflen, + int *statusp, void *extradata ); +static int prldap_getpeername( LDAP *ld, struct sockaddr *addr, + char *buffer, int buflen ); +static LDAPHostEnt *prldap_convert_hostent( LDAPHostEnt *ldhp, + PRHostEnt *prhp ); + + +/* + * Install NSPR DNS functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_dns_functions( LDAP *ld ) +{ + struct ldap_dns_fns dnsfns; + + memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) ); + dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE; + dnsfns.lddnsfn_gethostbyname = prldap_gethostbyname; + dnsfns.lddnsfn_gethostbyaddr = prldap_gethostbyaddr; + dnsfns.lddnsfn_getpeername = prldap_getpeername; + if ( ldap_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns ) != 0 ) { + return( -1 ); + } + + return( 0 ); +} + + +static LDAPHostEnt * +prldap_gethostbyname( const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ) +{ + PRHostEnt prhent; + + if( !statusp || ( *statusp = (int)PR_GetIPNodeByName( name, + PRLDAP_DEFAULT_ADDRESS_FAMILY, PR_AI_DEFAULT, + buffer, buflen, &prhent )) == PR_FAILURE ) { + return( NULL ); + } + + return( prldap_convert_hostent( result, &prhent )); +} + + +static LDAPHostEnt * +prldap_gethostbyaddr( const char *addr, int length, int type, + LDAPHostEnt *result, char *buffer, int buflen, int *statusp, + void *extradata ) +{ + PRHostEnt prhent; + PRNetAddr iaddr; + + if ( NULL == statusp ) { + return( NULL ); + } + + memset( &iaddr, 0, sizeof( iaddr )); + if ( PR_StringToNetAddr( addr, &iaddr ) == PR_FAILURE ) { + return( NULL ); + } + PRLDAP_SET_PORT( &iaddr, 0 ); + + if( PR_FAILURE == (*statusp = + PR_GetHostByAddr(&iaddr, buffer, buflen, &prhent ))) { + return( NULL ); + } + + return( prldap_convert_hostent( result, &prhent )); +} + + +static int +prldap_getpeername( LDAP *ld, struct sockaddr *addr, char *buffer, int buflen) +{ + PRLDAPIOSocketArg *sa; + PRNetAddr iaddr; + int ret; + + if (NULL != ld) { + ret = prldap_socket_arg_from_ld( ld, &sa ); + if (ret != LDAP_SUCCESS) { + return (-1); + } + ret = PR_GetPeerName(sa->prsock_prfd, &iaddr); + if( ret == PR_FAILURE ) { + return( -1 ); + } + *addr = *((struct sockaddr *)&iaddr.raw); + ret = PR_NetAddrToString(&iaddr, buffer, buflen); + if( ret == PR_FAILURE ) { + return( -1 ); + } + return (0); + } + return (-1); +} + + +/* + * Function: prldap_convert_hostent() + * Description: copy the fields of a PRHostEnt struct to an LDAPHostEnt + * Returns: the LDAPHostEnt pointer passed in. + */ +static LDAPHostEnt * +prldap_convert_hostent( LDAPHostEnt *ldhp, PRHostEnt *prhp ) +{ + ldhp->ldaphe_name = prhp->h_name; + ldhp->ldaphe_aliases = prhp->h_aliases; + ldhp->ldaphe_addrtype = prhp->h_addrtype; + ldhp->ldaphe_length = prhp->h_length; + ldhp->ldaphe_addr_list = prhp->h_addr_list; + return( ldhp ); +} diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-error.c b/ldap/c-sdk/libraries/libprldap/ldappr-error.c new file mode 100644 index 0000000000..b0cb6ed967 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-error.c @@ -0,0 +1,335 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Utilities for manageing the relationship between NSPR errors and + * OS (errno-style) errors. + * + * The overall strategy used is to map NSPR errors into OS errors. + */ + +#include "ldappr-int.h" + +void +prldap_set_system_errno( int oserrno ) +{ + PR_SetError( PR_GetError(), oserrno ); +} + + +int +prldap_get_system_errno( void ) +{ + return( PR_GetOSError()); +} + +/* + * Retrieve the NSPR error number, convert to a system error code, and return + * the result. + */ +struct prldap_errormap_entry { + PRInt32 erm_nspr; /* NSPR error code */ + int erm_system; /* corresponding system error code */ +}; + +/* XXX: not sure if this extra mapping for Windows is good or correct */ +#ifdef _WINDOWS +#ifndef ENOTSUP +#define ENOTSUP -1 +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EISCONN +#define EISCONN WSAEISCONN +#endif +#ifndef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif +#ifndef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef EOVERFLOW +#define EOVERFLOW -1 +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef ELOOP +#define ELOOP WSAELOOP +#endif +#ifndef ENOTBLK +#define ENOTBLK -1 +#endif +#ifndef ETXTBSY +#define ETXTBSY -1 +#endif +#ifndef ENETDOWN +#define ENETDOWN WSAENETDOWN +#endif +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif +#endif /* _WINDOWS */ + +#if defined(macintosh) +/* + * Some Unix error defs. Under CW 7, we can't define OTUNIXERRORS because + * it generates many conflicts with errno.h. Define what we need here. + * These need to be in sync with OpenTransport.h + */ +#define EWOULDBLOCK 35 +#define ENOTSOCK 38 +#define EPROTOTYPE 41 +#define EPROTONOSUPPORT 43 +#define EOPNOTSUPP 45 +#define EADDRINUSE 48 +#define EADDRNOTAVAIL 49 +#define ENETDOWN 50 +#define ECONNABORTED 53 +#define ECONNRESET 54 +#define EISCONN 56 +#define ENOTCONN 57 +#define ESHUTDOWN 58 +#define ETIMEDOUT 60 +#define ECONNREFUSED 61 +#define EHOSTUNREACH 65 +#define EAFNOSUPPORT -1 +#define ELOOP -1 +#define ENOTBLK -1 +#define ENOTSUP -1 +#define EOVERFLOW -1 +#define ETXTBSY -1 +#endif /* macintosh */ + +#ifdef XP_OS2 +#define SOCBASEERR 0 +#endif +#ifndef ENOTSUP +#define ENOTSUP -1 +#endif +#ifndef EOVERFLOW +#define EOVERFLOW -1 +#endif +#ifndef EDEADLOCK +#define EDEADLOCK -1 +#endif +#ifndef EFAULT +#define EFAULT SOCEFAULT +#endif +#ifndef EPIPE +#define EPIPE SOCEPIPE +#endif +#ifndef EIO +#define EIO (SOCBASEERR+5) +#endif +#ifndef EDEADLK +#define EDEADLK (SOCBASEERR+11) +#endif +#ifndef ENOTBLK +#define ENOTBLK (SOCBASEERR+15) +#endif +#ifndef EBUSY +#define EBUSY (SOCBASEERR+16) +#endif +#ifndef ENOTDIR +#define ENOTDIR (SOCBASEERR+20) +#endif +#ifndef EISDIR +#define EISDIR (SOCBASEERR+21) +#endif +#ifndef ENFILE +#define ENFILE (SOCBASEERR+23) +#endif +#ifndef ETXTBSY +#define ETXTBSY (SOCBASEERR+26) +#endif +#ifndef EFBIG +#define EFBIG (SOCBASEERR+27) +#endif +#ifndef ESPIPE +#define ESPIPE (SOCBASEERR+29) +#endif +#ifndef EROFS +#define EROFS (SOCBASEERR+30) +#endif + +#ifdef BEOS +#define ENOTSUP -1 +#define ENOTBLK -1 +#define ETXTBSY -1 +#endif + +#if defined(BSDI) || defined(OPENBSD) || defined (NETBSD) +#define ENOTSUP -1 +#endif + +#if defined(OSF1) || defined(BSDI) || defined(VMS) || defined(OPENBSD) +#define EOVERFLOW -1 +#endif + +#if defined(__hpux) || defined(_AIX) || defined(OSF1) || defined(DARWIN) || \ + defined(BEOS) || defined(FREEBSD) || defined(BSDI) || defined(VMS) || \ + defined(OPENBSD) || defined(NETBSD) +#define EDEADLOCK -1 +#endif + +/* XXX: need to verify that the -1 entries are correct (no mapping) */ +static struct prldap_errormap_entry prldap_errormap[] = { + { PR_OUT_OF_MEMORY_ERROR, ENOMEM }, + { PR_BAD_DESCRIPTOR_ERROR, EBADF }, + { PR_WOULD_BLOCK_ERROR, EAGAIN }, + { PR_ACCESS_FAULT_ERROR, EFAULT }, + { PR_INVALID_METHOD_ERROR, EINVAL }, /* XXX: correct mapping ? */ + { PR_ILLEGAL_ACCESS_ERROR, EACCES }, /* XXX: correct mapping ? */ + { PR_UNKNOWN_ERROR, -1 }, + { PR_PENDING_INTERRUPT_ERROR, -1 }, + { PR_NOT_IMPLEMENTED_ERROR, ENOTSUP }, + { PR_IO_ERROR, EIO }, + { PR_IO_TIMEOUT_ERROR, ETIMEDOUT }, /* XXX: correct mapping ? */ + { PR_IO_PENDING_ERROR, -1 }, + { PR_DIRECTORY_OPEN_ERROR, ENOTDIR }, + { PR_INVALID_ARGUMENT_ERROR, EINVAL }, + { PR_ADDRESS_NOT_AVAILABLE_ERROR, EADDRNOTAVAIL }, + { PR_ADDRESS_NOT_SUPPORTED_ERROR, EAFNOSUPPORT }, + { PR_IS_CONNECTED_ERROR, EISCONN }, + { PR_BAD_ADDRESS_ERROR, EFAULT }, /* XXX: correct mapping ? */ + { PR_ADDRESS_IN_USE_ERROR, EADDRINUSE }, + { PR_CONNECT_REFUSED_ERROR, ECONNREFUSED }, + { PR_NETWORK_UNREACHABLE_ERROR, EHOSTUNREACH }, + { PR_CONNECT_TIMEOUT_ERROR, ETIMEDOUT }, + { PR_NOT_CONNECTED_ERROR, ENOTCONN }, + { PR_LOAD_LIBRARY_ERROR, -1 }, + { PR_UNLOAD_LIBRARY_ERROR, -1 }, + { PR_FIND_SYMBOL_ERROR, -1 }, + { PR_INSUFFICIENT_RESOURCES_ERROR, -1 }, + { PR_DIRECTORY_LOOKUP_ERROR, EHOSTUNREACH },/* an approximation */ + { PR_TPD_RANGE_ERROR, -1 }, + { PR_PROC_DESC_TABLE_FULL_ERROR, -1 }, + { PR_SYS_DESC_TABLE_FULL_ERROR, -1 }, + { PR_NOT_SOCKET_ERROR, ENOTSOCK }, + { PR_NOT_TCP_SOCKET_ERROR, EPROTOTYPE }, + { PR_SOCKET_ADDRESS_IS_BOUND_ERROR, -1 }, + { PR_NO_ACCESS_RIGHTS_ERROR, EACCES }, /* XXX: correct mapping ? */ + { PR_OPERATION_NOT_SUPPORTED_ERROR, EOPNOTSUPP }, + { PR_PROTOCOL_NOT_SUPPORTED_ERROR, EPROTONOSUPPORT }, + { PR_REMOTE_FILE_ERROR, -1 }, + { PR_BUFFER_OVERFLOW_ERROR, EOVERFLOW }, + { PR_CONNECT_RESET_ERROR, ECONNRESET }, + { PR_RANGE_ERROR, ERANGE }, + { PR_DEADLOCK_ERROR, EDEADLK }, + { PR_FILE_IS_LOCKED_ERROR, EDEADLOCK }, /* XXX: correct mapping ? */ + { PR_FILE_TOO_BIG_ERROR, EFBIG }, + { PR_NO_DEVICE_SPACE_ERROR, ENOSPC }, + { PR_PIPE_ERROR, EPIPE }, + { PR_NO_SEEK_DEVICE_ERROR, ESPIPE }, + { PR_IS_DIRECTORY_ERROR, EISDIR }, + { PR_LOOP_ERROR, ELOOP }, + { PR_NAME_TOO_LONG_ERROR, ENAMETOOLONG }, + { PR_FILE_NOT_FOUND_ERROR, ENOENT }, + { PR_NOT_DIRECTORY_ERROR, ENOTDIR }, + { PR_READ_ONLY_FILESYSTEM_ERROR, EROFS }, + { PR_DIRECTORY_NOT_EMPTY_ERROR, ENOTEMPTY }, + { PR_FILESYSTEM_MOUNTED_ERROR, EBUSY }, + { PR_NOT_SAME_DEVICE_ERROR, EXDEV }, + { PR_DIRECTORY_CORRUPTED_ERROR, -1 }, + { PR_FILE_EXISTS_ERROR, EEXIST }, + { PR_MAX_DIRECTORY_ENTRIES_ERROR, -1 }, + { PR_INVALID_DEVICE_STATE_ERROR, ENOTBLK }, /* XXX: correct mapping ? */ + { PR_DEVICE_IS_LOCKED_ERROR, -2 }, + { PR_NO_MORE_FILES_ERROR, ENFILE }, + { PR_END_OF_FILE_ERROR, -1 }, + { PR_FILE_SEEK_ERROR, ESPIPE }, /* XXX: correct mapping ? */ + { PR_FILE_IS_BUSY_ERROR, ETXTBSY }, + { PR_OPERATION_ABORTED_ERROR, -1 }, + { PR_IN_PROGRESS_ERROR, -1 }, + { PR_ALREADY_INITIATED_ERROR, -1 }, + { PR_GROUP_EMPTY_ERROR, -1 }, + { PR_INVALID_STATE_ERROR, -1 }, + { PR_NETWORK_DOWN_ERROR, ENETDOWN }, + { PR_SOCKET_SHUTDOWN_ERROR, ESHUTDOWN }, + { PR_CONNECT_ABORTED_ERROR, ECONNABORTED }, + { PR_HOST_UNREACHABLE_ERROR, EHOSTUNREACH }, + { PR_MAX_ERROR, -1 }, +}; + + +int +prldap_prerr2errno( void ) +{ + int oserr, i; + PRInt32 nsprerr; + + nsprerr = PR_GetError(); + + oserr = -1; /* unknown */ + for ( i = 0; prldap_errormap[i].erm_nspr != PR_MAX_ERROR; ++i ) { + if ( prldap_errormap[i].erm_nspr == nsprerr ) { + oserr = prldap_errormap[i].erm_system; + break; + } + } + + return( oserr ); +} diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-int.h b/ldap/c-sdk/libraries/libprldap/ldappr-int.h new file mode 100644 index 0000000000..ed4bfbce61 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-int.h @@ -0,0 +1,139 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Internal header for libprldap -- glue NSPR (Netscape Portable Runtime) + * to libldap. + * + */ + +#include "ldap.h" +#include "nspr.h" +#include "ldappr.h" + +#include +#include +#include + +/* + * Macros: + */ + +/* #define PRLDAP_DEBUG 1 */ /* uncomment to enable debugging printfs */ + +/* + * All of the sockets we use are IPv6 capable. + * Change the following #define to PR_AF_INET to support IPv4 only. + */ +#define PRLDAP_DEFAULT_ADDRESS_FAMILY PR_AF_INET6 + +/* + * Max length for sending message with one PR_Send. + * If a single message is larger than this size, the message is divided + * into multiple pieces up to this length and sent out. This is necessary + * on Microsoft Windows at least where attempts to send really large + * messages in one PR_Send() call result in an error. + */ +#define PRLDAP_MAX_SEND_SIZE (8*1024*1024) /* 8MB */ + +/* + * Macro to set port to the 'port' field of a NSPR PRNetAddr union. + ** INPUTS: + ** PRNetAddr *myaddr A network address. + ** PRUint16 myport port to set to the 'port' field of 'addr'. + ** RETURN: none + */ +#define PRLDAP_SET_PORT(myaddr,myport) \ + ((myaddr)->raw.family == PR_AF_INET6 ? ((myaddr)->ipv6.port = PR_htons(myport)) : ((myaddr)->inet.port = PR_htons(myport))) + +/* + * Data structures: + */ + +/* data structure that populates the I/O callback session arg. */ +typedef struct lextiof_session_private { + PRPollDesc *prsess_pollds; /* for poll callback */ + int prsess_pollds_count; /* # of elements in pollds */ + int prsess_io_max_timeout; /* in milliseconds */ + void *prsess_appdata; /* application specific data */ +} PRLDAPIOSessionArg; + +/* data structure that populates the I/O callback socket-specific arg. */ +typedef struct lextiof_socket_private { + PRFileDesc *prsock_prfd; /* associated NSPR file desc. */ + int prsock_io_max_timeout; /* in milliseconds */ + void *prsock_appdata; /* application specific data */ +} PRLDAPIOSocketArg; + + +/* + * Function prototypes: + */ + +/* + * From ldapprio.c: + */ +int prldap_install_io_functions( LDAP *ld, int shared ); +int prldap_session_arg_from_ld( LDAP *ld, PRLDAPIOSessionArg **sessargpp ); +int prldap_set_io_max_timeout( PRLDAPIOSessionArg *prsessp, + int io_max_timeout ); +int prldap_get_io_max_timeout( PRLDAPIOSessionArg *prsessp, + int *io_max_timeoutp ); +int prldap_socket_arg_from_ld( LDAP *ld, PRLDAPIOSocketArg **sockargpp ); +PRLDAPIOSocketArg *prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg ); + + +/* + * From ldapprthreads.c: + */ +int prldap_install_thread_functions( LDAP *ld, int shared ); +int prldap_thread_new_handle( LDAP *ld, void *sessionarg ); +void prldap_thread_dispose_handle( LDAP *ld, void *sessionarg ); + + +/* + * From ldapprdns.c: + */ +int prldap_install_dns_functions( LDAP *ld ); + + +/* + * From ldapprerror.c: + */ +void prldap_set_system_errno( int e ); +int prldap_get_system_errno( void ); +int prldap_prerr2errno( void ); diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-io.c b/ldap/c-sdk/libraries/libprldap/ldappr-io.c new file mode 100644 index 0000000000..863545146a --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-io.c @@ -0,0 +1,744 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Extended I/O callback functions for libldap that use + * NSPR (Netscape Portable Runtime) I/O. + * + * High level strategy: we use the socket-specific arg to hold our own data + * structure that includes the NSPR file handle (PRFileDesc *), among other + * useful information. We use the default argument to hold an LDAP session + * handle specific data structure. + */ + +#include "ldappr-int.h" + +#define PRLDAP_POLL_ARRAY_GROWTH 5 /* grow arrays 5 elements at a time */ + +/* + * Local function prototypes: + */ +static PRIntervalTime prldap_timeout2it( int ms_timeout, int ms_maxtimeout ); +static int LDAP_CALLBACK prldap_read( int s, void *buf, int bufsize, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_write( int s, const void *buf, int len, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_poll( LDAP_X_PollFD fds[], int nfds, + int timeout, struct lextiof_session_private *sessionarg ); +static int LDAP_CALLBACK prldap_connect( const char *hostlist, int defport, + int timeout, unsigned long options, + struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ); +static int LDAP_CALLBACK prldap_close( int s, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_newhandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static void LDAP_CALLBACK prldap_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static int LDAP_CALLBACK prldap_shared_newhandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static void LDAP_CALLBACK prldap_shared_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static PRLDAPIOSessionArg *prldap_session_arg_alloc( void ); +static void prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp ); +static void prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp ); +static void *prldap_safe_realloc( void *ptr, PRUint32 size ); + + + +/* + * Local macros: + */ +/* given a socket-specific arg, return the corresponding PRFileDesc * */ +#define PRLDAP_GET_PRFD( socketarg ) \ + (((PRLDAPIOSocketArg *)(socketarg))->prsock_prfd) + +/* + * Static variables. + */ +static int prldap_default_io_max_timeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; + + +/* + * Install NSPR I/O functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_io_functions( LDAP *ld, int shared ) +{ + struct ldap_x_ext_io_fns iofns; + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + iofns.lextiof_read = prldap_read; + iofns.lextiof_write = prldap_write; + iofns.lextiof_poll = prldap_poll; + iofns.lextiof_connect = prldap_connect; + iofns.lextiof_close = prldap_close; + if ( shared ) { + iofns.lextiof_newhandle = prldap_shared_newhandle; + iofns.lextiof_disposehandle = prldap_shared_disposehandle; + } else { + iofns.lextiof_newhandle = prldap_newhandle; + iofns.lextiof_disposehandle = prldap_disposehandle; + } + if ( NULL != ld ) { + /* + * If we are dealing with a real ld, we allocate the session specific + * data structure now. If not allocated here, it will be allocated + * inside prldap_newhandle() or prldap_shared_newhandle(). + */ + if ( NULL == + ( iofns.lextiof_session_arg = prldap_session_arg_alloc())) { + ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + } else { + iofns.lextiof_session_arg = NULL; + } + + if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &iofns ) != 0 ) { + prldap_session_arg_free( + (PRLDAPIOSessionArg **) &iofns.lextiof_session_arg ); + return( -1 ); + } + + return( 0 ); +} + + +static PRIntervalTime +prldap_timeout2it( int ms_timeout, int ms_maxtimeout ) +{ + PRIntervalTime prit; + + if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_timeout ) { + prit = PR_INTERVAL_NO_WAIT; + } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout ) { + prit = PR_INTERVAL_NO_TIMEOUT; + } else { + prit = PR_MillisecondsToInterval( ms_timeout ); + } + + /* cap at maximum I/O timeout */ + if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_maxtimeout ) { + prit = LDAP_X_IO_TIMEOUT_NO_WAIT; + } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT != ms_maxtimeout ) { + if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout || + ms_timeout > ms_maxtimeout ) { + prit = PR_MillisecondsToInterval( ms_maxtimeout ); + } + } + +#ifdef PRLDAP_DEBUG + if ( PR_INTERVAL_NO_WAIT == prit ) { + fprintf( stderr, "prldap_timeout2it: NO_WAIT\n" ); + } else if ( PR_INTERVAL_NO_TIMEOUT == prit ) { + fprintf( stderr, "prldap_timeout2it: NO_TIMEOUT\n" ); + } else { + fprintf( stderr, "prldap_timeout2it: %dms\n", + PR_IntervalToMilliseconds(prit)); + } +#endif /* PRLDAP_DEBUG */ + + return( prit ); +} + + +static int LDAP_CALLBACK +prldap_read( int s, void *buf, int bufsize, + struct lextiof_socket_private *socketarg ) +{ + PRIntervalTime prit; + + prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT, + socketarg->prsock_io_max_timeout ); + return( PR_Recv( PRLDAP_GET_PRFD(socketarg), buf, bufsize, 0, prit )); +} + + +static int LDAP_CALLBACK +prldap_write( int s, const void *buf, int len, + struct lextiof_socket_private *socketarg ) +{ + PRIntervalTime prit; + char *ptr = (char *)buf; + int rest = len; + + prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT, + socketarg->prsock_io_max_timeout ); + + while ( rest > 0 ) { + int rval; + if ( rest > PRLDAP_MAX_SEND_SIZE ) { + len = PRLDAP_MAX_SEND_SIZE; + } else { + len = rest; + } + /* + * Note the 4th parameter (flags) to PR_Send() has been obsoleted and + * must always be 0 + */ + rval = PR_Send( PRLDAP_GET_PRFD(socketarg), ptr, len, 0, prit ); + if ( 0 > rval ) { + return rval; + } + + if ( 0 == rval ) { + break; + } + + ptr += rval; + rest -= rval; + } + + return (int)( ptr - (char *)buf ); +} + + +struct prldap_eventmap_entry { + PRInt16 evm_nspr; /* corresponding NSPR PR_Poll() event */ + int evm_ldap; /* LDAP poll event */ +}; + +static struct prldap_eventmap_entry prldap_eventmap[] = { + { PR_POLL_READ, LDAP_X_POLLIN }, + { PR_POLL_EXCEPT, LDAP_X_POLLPRI }, + { PR_POLL_WRITE, LDAP_X_POLLOUT }, + { PR_POLL_ERR, LDAP_X_POLLERR }, + { PR_POLL_HUP, LDAP_X_POLLHUP }, + { PR_POLL_NVAL, LDAP_X_POLLNVAL }, +}; + +#define PRLDAP_EVENTMAP_ENTRIES \ + sizeof(prldap_eventmap)/sizeof(struct prldap_eventmap_entry ) + +static int LDAP_CALLBACK +prldap_poll( LDAP_X_PollFD fds[], int nfds, int timeout, + struct lextiof_session_private *sessionarg ) +{ + PRLDAPIOSessionArg *prsessp = sessionarg; + PRPollDesc *pds; + int i, j, rc; + + if ( NULL == prsessp ) { + prldap_set_system_errno( EINVAL ); + return( -1 ); + } + + /* allocate or resize NSPR poll descriptor array */ + if ( prsessp->prsess_pollds_count < nfds ) { + pds = prldap_safe_realloc( prsessp->prsess_pollds, + ( nfds + PRLDAP_POLL_ARRAY_GROWTH ) + * sizeof( PRPollDesc )); + if ( NULL == pds ) { + prldap_set_system_errno( prldap_prerr2errno()); + return( -1 ); + } + prsessp->prsess_pollds = pds; + prsessp->prsess_pollds_count = nfds + PRLDAP_POLL_ARRAY_GROWTH; + } else { + pds = prsessp->prsess_pollds; + } + + /* populate NSPR poll info. based on LDAP info. */ + for ( i = 0; i < nfds; ++i ) { + if ( NULL == fds[i].lpoll_socketarg ) { + pds[i].fd = NULL; + } else { + pds[i].fd = PRLDAP_GET_PRFD( fds[i].lpoll_socketarg ); + } + pds[i].in_flags = pds[i].out_flags = 0; + if ( fds[i].lpoll_fd >= 0 ) { + for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) { + if (( fds[i].lpoll_events & prldap_eventmap[j].evm_ldap ) + != 0 ) { + pds[i].in_flags |= prldap_eventmap[j].evm_nspr; + } + } + } + fds[i].lpoll_revents = 0; /* clear revents */ + } + + /* call PR_Poll() to do the real work */ + rc = PR_Poll( pds, nfds, + prldap_timeout2it( timeout, prsessp->prsess_io_max_timeout )); + + /* populate LDAP info. based on NSPR results */ + for ( i = 0; i < nfds; ++i ) { + if ( pds[i].fd != NULL ) { + for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) { + if (( pds[i].out_flags & prldap_eventmap[j].evm_nspr ) + != 0 ) { + fds[i].lpoll_revents |= prldap_eventmap[j].evm_ldap; + } + } + } + } + + return( rc ); +} + + +/* + * Utility function to try one TCP connect() + * Returns 1 if successful and -1 if not. Sets the NSPR fd inside prsockp. + */ +static int +prldap_try_one_address( struct lextiof_socket_private *prsockp, + PRNetAddr *addrp, int timeout, unsigned long options ) +{ + /* + * Open a TCP socket: + */ + if (( prsockp->prsock_prfd = PR_OpenTCPSocket( + PR_NetAddrFamily(addrp) )) == NULL ) { + return( -1 ); + } + + /* + * Set nonblocking option if requested: + */ + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_NONBLOCKING )) { + PRSocketOptionData optdata; + + optdata.option = PR_SockOpt_Nonblocking; + optdata.value.non_blocking = PR_TRUE; + if ( PR_SetSocketOption( prsockp->prsock_prfd, &optdata ) + != PR_SUCCESS ) { + prldap_set_system_errno( prldap_prerr2errno()); + PR_Close( prsockp->prsock_prfd ); + return( -1 ); + } + } + +#ifdef PRLDAP_DEBUG + { + char buf[ 256 ], *p, *fmtstr; + + if ( PR_SUCCESS != PR_NetAddrToString( addrp, buf, sizeof(buf ))) { + strcpy( buf, "conversion failed!" ); + } + if ( strncmp( buf, "::ffff:", 7 ) == 0 ) { + /* IPv4 address mapped into IPv6 address space */ + p = buf + 7; + fmtstr = "prldap_try_one_address(): Trying %s:%d...\n"; + } else { + p = buf; + fmtstr = "prldap_try_one_address(): Trying [%s]:%d...\n"; + } + fprintf( stderr, fmtstr, p, PR_ntohs( addrp->ipv6.port )); + } +#endif /* PRLDAP_DEBUG */ + + /* + * Try to open the TCP connection itself: + */ + if ( PR_SUCCESS != PR_Connect( prsockp->prsock_prfd, addrp, + prldap_timeout2it( timeout, prsockp->prsock_io_max_timeout )) + && PR_IN_PROGRESS_ERROR != PR_GetError() ) { + PR_Close( prsockp->prsock_prfd ); + prsockp->prsock_prfd = NULL; + return( -1 ); + } + +#ifdef PRLDAP_DEBUG + fputs( "prldap_try_one_address(): Connected.\n", stderr ); +#endif /* PRLDAP_DEBUG */ + + /* + * Success. Return a valid file descriptor (1 is always valid) + */ + return( 1 ); +} + + +/* + * XXXmcs: At present, this code ignores the timeout when doing DNS lookups. + */ +static int LDAP_CALLBACK +prldap_connect( const char *hostlist, int defport, int timeout, + unsigned long options, struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ) +{ + int rc, parse_err, port; + char *host; + struct ldap_x_hostlist_status *status; + struct lextiof_socket_private *prsockp; + PRNetAddr addr; + PRAddrInfo *infop = NULL; + + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { + prldap_set_system_errno( EINVAL ); + return( -1 ); + } + + if ( NULL == ( prsockp = prldap_socket_arg_alloc( sessionarg ))) { + prldap_set_system_errno( prldap_prerr2errno()); + return( -1 ); + } + + rc = -1; /* pessimistic */ + for ( parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port, + &status ); + rc < 0 && LDAP_SUCCESS == parse_err && NULL != host; + parse_err = ldap_x_hostlist_next( &host, &port, status )) { + /* + * First, call PR_GetAddrInfoByName; PR_GetAddrInfoByName could + * support both IPv4 and IPv6 addresses depending upon the system's + * configuration. All available addresses are returned and each of + * them is examined in prldap_try_one_address till it succeeds. + * Then, try converting the string address, in case the string + * address was not successfully handled in PR_GetAddrInfoByName. + */ + if ( NULL != ( infop = + PR_GetAddrInfoByName( host, PR_AF_UNSPEC, + (PR_AI_ADDRCONFIG|PR_AI_NOCANONNAME) ))) { + void *enump = NULL; + do { + memset( &addr, 0, sizeof( addr )); + enump = PR_EnumerateAddrInfo( enump, infop, port, &addr ); + if ( NULL == enump ) { + break; + } + rc = prldap_try_one_address( prsockp, &addr, timeout, options ); + } while ( rc < 0 ); + PR_FreeAddrInfo( infop ); + } else if ( PR_SUCCESS == PR_StringToNetAddr( host, &addr )) { + PRLDAP_SET_PORT( &addr, port ); + rc = prldap_try_one_address( prsockp, &addr, timeout, options ); + } + ldap_memfree( host ); + } + + if ( host ) { + ldap_memfree( host ); + } + ldap_x_hostlist_statusfree( status ); + + if ( rc < 0 ) { + prldap_set_system_errno( prldap_prerr2errno()); + prldap_socket_arg_free( &prsockp ); + } else { + *socketargp = prsockp; + } + + return( rc ); +} + + +static int LDAP_CALLBACK +prldap_close( int s, struct lextiof_socket_private *socketarg ) +{ + int rc; + + rc = 0; + if ( PR_Close( PRLDAP_GET_PRFD(socketarg)) != PR_SUCCESS ) { + rc = -1; + prldap_set_system_errno( prldap_prerr2errno()); + } + prldap_socket_arg_free( &socketarg ); + + return( rc ); +} + + +/* + * LDAP session handle creation callback. + * + * Allocate a session argument if not already done, and then call the + * thread's new handle function. + */ +static int LDAP_CALLBACK +prldap_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + + if ( NULL == sessionarg ) { + struct ldap_x_ext_io_fns iofns; + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, + (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + if ( NULL == + ( iofns.lextiof_session_arg = prldap_session_arg_alloc())) { + return( LDAP_NO_MEMORY ); + } + if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, + (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + } + + return( LDAP_SUCCESS ); +} + + +/* only called/installed if shared is non-zero. */ +static int LDAP_CALLBACK +prldap_shared_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + int rc; + + if (( rc = prldap_newhandle( ld, sessionarg )) == LDAP_SUCCESS ) { + rc = prldap_thread_new_handle( ld, sessionarg ); + } + + return( rc ); +} + + +static void LDAP_CALLBACK +prldap_disposehandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + prldap_session_arg_free( &sessionarg ); +} + + +/* only called/installed if shared is non-zero */ +static void LDAP_CALLBACK +prldap_shared_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ) +{ + prldap_thread_dispose_handle( ld, sessionarg ); + prldap_disposehandle( ld, sessionarg ); +} + + +/* + * Allocate a session argument. + */ +static PRLDAPIOSessionArg * +prldap_session_arg_alloc( void ) +{ + PRLDAPIOSessionArg *prsessp; + + prsessp = PR_Calloc( 1, sizeof( PRLDAPIOSessionArg )); + + if ( NULL != prsessp ) { + /* copy global defaults to the new session handle */ + prsessp->prsess_io_max_timeout = prldap_default_io_max_timeout; + } + + return( prsessp ); +} + + +static void +prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp ) +{ + if ( NULL != prsesspp && NULL != *prsesspp ) { + if ( NULL != (*prsesspp)->prsess_pollds ) { + PR_Free( (*prsesspp)->prsess_pollds ); + (*prsesspp)->prsess_pollds = NULL; + } + PR_Free( *prsesspp ); + *prsesspp = NULL; + } +} + + +/* + * Given an LDAP session handle, retrieve a session argument. + * Returns an LDAP error code. + */ +int +prldap_session_arg_from_ld( LDAP *ld, PRLDAPIOSessionArg **sessargpp ) +{ + struct ldap_x_ext_io_fns iofns; + + if ( NULL == ld || NULL == sessargpp ) { + /* XXXmcs: NULL ld's are not supported */ + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + if ( NULL == iofns.lextiof_session_arg ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + *sessargpp = iofns.lextiof_session_arg; + return( LDAP_SUCCESS ); +} + + +/* + * Given an LDAP session handle, retrieve a socket argument. + * Returns an LDAP error code. + */ +int +prldap_socket_arg_from_ld( LDAP *ld, PRLDAPIOSocketArg **sockargpp ) +{ + Sockbuf *sbp; + struct lber_x_ext_io_fns extiofns; + + if ( NULL == ld || NULL == sockargpp ) { + /* XXXmcs: NULL ld's are not supported */ + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( ldap_get_option( ld, LDAP_X_OPT_SOCKBUF, (void *)&sbp ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + memset( &extiofns, 0, sizeof(extiofns)); + extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + if ( ber_sockbuf_get_option( sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&extiofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + if ( NULL == extiofns.lbextiofn_socket_arg ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + *sockargpp = extiofns.lbextiofn_socket_arg; + return( LDAP_SUCCESS ); +} + + +/* + * Allocate a socket argument. + */ +PRLDAPIOSocketArg * +prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg ) +{ + PRLDAPIOSocketArg *prsockp; + + prsockp = PR_Calloc( 1, sizeof( PRLDAPIOSocketArg )); + + if ( NULL != prsockp && NULL != sessionarg ) { + /* copy socket defaults from the session */ + prsockp->prsock_io_max_timeout = sessionarg->prsess_io_max_timeout; + } + + return( prsockp ); +} + + +static void +prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp ) +{ + if ( NULL != prsockpp && NULL != *prsockpp ) { + PR_Free( *prsockpp ); + *prsockpp = NULL; + } +} + + +static void * +prldap_safe_realloc( void *ptr, PRUint32 size ) +{ + void *p; + + if ( NULL == ptr ) { + p = PR_Malloc( size ); + } else { + p = PR_Realloc( ptr, size ); + } + + return( p ); +} + + + +/* returns an LDAP result code */ +int +prldap_set_io_max_timeout( PRLDAPIOSessionArg *prsessp, int io_max_timeout ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + + if ( NULL == prsessp ) { + prldap_default_io_max_timeout = io_max_timeout; + } else { + prsessp->prsess_io_max_timeout = io_max_timeout; + } + + return( rc ); +} + + +/* returns an LDAP result code */ +int +prldap_get_io_max_timeout( PRLDAPIOSessionArg *prsessp, int *io_max_timeoutp ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + + if ( NULL == io_max_timeoutp ) { + rc = LDAP_PARAM_ERROR; + } else if ( NULL == prsessp ) { + *io_max_timeoutp = prldap_default_io_max_timeout; + } else { + *io_max_timeoutp = prsessp->prsess_io_max_timeout; + } + + return( rc ); +} + +/* Check if NSPR layer has been installed for a LDAP session. + * Simply check whether prldap_connect() I/O function is installed + */ +PRBool +prldap_is_installed( LDAP *ld ) +{ + struct ldap_x_ext_io_fns iofns; + + /* Retrieve current I/O functions */ + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if ( ld == NULL || ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) + != 0 || iofns.lextiof_connect != prldap_connect ) + { + return( PR_FALSE ); + } + + return( PR_TRUE ); +} + diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-public.c b/ldap/c-sdk/libraries/libprldap/ldappr-public.c new file mode 100644 index 0000000000..6e9e597e6a --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-public.c @@ -0,0 +1,454 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Public interface for libprldap -- use NSPR (Netscape Portable Runtime) + * I/O, threads, etc. with libldap. + * + */ + +#include "ldappr-int.h" +#include + +/* + * Function: prldap_init(). + * + * Create a new LDAP session handle, but with NSPR I/O, threading, and DNS + * functions installed. + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * prldap_init() returns an LDAP session handle (or NULL if an error occurs). + */ +LDAP * LDAP_CALL +prldap_init( const char *defhost, int defport, int shared ) +{ + LDAP *ld; + + if (( ld = ldap_init( defhost, defport )) != NULL ) { + if ( prldap_install_routines( ld, shared ) != LDAP_SUCCESS ) { + prldap_set_system_errno( EINVAL ); /* XXXmcs: just a guess! */ + ldap_unbind( ld ); + ld = NULL; + } + } + + return( ld ); +} + + +/* + * Function: prldap_install_routines(). + * + * Install NSPR I/O, threading, and DNS functions so they will be used by + * 'ld'. + * + * If 'ld' is NULL, the functions are installed as the default functions + * for all new LDAP * handles). + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * prldap_install_routines() returns an LDAP API error code (LDAP_SUCCESS + * if all goes well). + */ +int LDAP_CALL +prldap_install_routines( LDAP *ld, int shared ) +{ + + if ( prldap_install_io_functions( ld, shared ) != 0 + || prldap_install_thread_functions( ld, shared ) != 0 + || prldap_install_dns_functions( ld ) != 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_set_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * SOCKET, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, set + * an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the option is set as the default + * for all new prldap sessions. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL +prldap_set_session_option( LDAP *ld, void *sessionarg, int option, ... ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + PRLDAPIOSessionArg *prsessp = NULL; + va_list ap; + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } + + va_start( ap, option ); + switch ( option ) { + case PRLDAP_OPT_IO_MAX_TIMEOUT: + rc = prldap_set_io_max_timeout( prsessp, va_arg( ap, int )); + break; + default: + rc = LDAP_PARAM_ERROR; + } + va_end( ap ); + + return( rc ); +} + + +/* + * Function: prldap_get_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * SOCKET, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, retrieve + * the setting for an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the default option value for all new + * new prldap sessions is retrieved. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_get_session_option( LDAP *ld, void *sessionarg, + int option, ... ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + PRLDAPIOSessionArg *prsessp = NULL; + va_list ap; + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } + + va_start( ap, option ); + switch ( option ) { + case PRLDAP_OPT_IO_MAX_TIMEOUT: + rc = prldap_get_io_max_timeout( prsessp, va_arg( ap, int * )); + break; + default: + rc = LDAP_PARAM_ERROR; + } + va_end( ap ); + + return( rc ); +} + + +/* + * Function: prldap_set_session_info(). + * + * Given an LDAP session handle, set some application-specific data. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL +prldap_set_session_info( LDAP *ld, void *sessionarg, PRLDAPSessionInfo *seip ) +{ + int rc; + PRLDAPIOSessionArg *prsessp; + + if ( seip == NULL || PRLDAP_SESSIONINFO_SIZE != seip->seinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + prsessp->prsess_appdata = seip->seinfo_appdata; + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_get_session_info(). + * + * Given an LDAP session handle, retrieve some application-specific data. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that seip points to are filled in). + */ +int LDAP_CALL +prldap_get_session_info( LDAP *ld, void *sessionarg, PRLDAPSessionInfo *seip ) +{ + int rc; + PRLDAPIOSessionArg *prsessp; + + if ( seip == NULL || PRLDAP_SESSIONINFO_SIZE != seip->seinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + seip->seinfo_appdata = prsessp->prsess_appdata; + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_set_socket_info(). + * + * Given an integer fd and a void * argument such as those passed to the + * extended I/O callback functions, set socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + * + * Note: it is only safe to change soinfo_prfd from within the SOCKET + * extended I/O callback function. + */ +int LDAP_CALL +prldap_set_socket_info( int fd, void *socketarg, PRLDAPSocketInfo *soip ) +{ + PRLDAPIOSocketArg *prsockp; + + if ( NULL == socketarg || NULL == soip || + PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + return( LDAP_PARAM_ERROR ); + } + + prsockp = (PRLDAPIOSocketArg *)socketarg; + prsockp->prsock_prfd = soip->soinfo_prfd; + prsockp->prsock_appdata = soip->soinfo_appdata; + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_get_socket_info(). + * + * Given an integer fd and a void * argument such as those passed to the + * extended I/O callback functions, retrieve socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_get_socket_info( int fd, void *socketarg, PRLDAPSocketInfo *soip ) +{ + PRLDAPIOSocketArg *prsockp; + + if ( NULL == socketarg || NULL == soip || + PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + return( LDAP_PARAM_ERROR ); + } + + prsockp = (PRLDAPIOSocketArg *)socketarg; + soip->soinfo_prfd = prsockp->prsock_prfd; + soip->soinfo_appdata = prsockp->prsock_appdata; + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_get_default_socket_info(). + * + * Given an LDAP session handle, retrieve socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_get_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ) +{ + int rc; + PRLDAPIOSocketArg *prsockp; + + + if ( NULL == soip || PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_socket_arg_from_ld( ld, &prsockp ))) { + return( rc ); + } + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + soip->soinfo_prfd = prsockp->prsock_prfd; + soip->soinfo_appdata = prsockp->prsock_appdata; + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_set_default_socket_info(). + * + * Given an LDAP session handle, set socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_set_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ) +{ + int rc; + PRLDAPIOSocketArg *prsockp; + + + if ( NULL == soip || PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_socket_arg_from_ld( ld, &prsockp ))) { + return( rc ); + } + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + prsockp->prsock_prfd = soip->soinfo_prfd; + prsockp->prsock_appdata = soip->soinfo_appdata; + + return( LDAP_SUCCESS ); +} + + +/* +* Function: prldap_import_connection(). +* +* Given the LDAP handle the connection parameters for the +* file descriptor are imported into NSPR layer. +* +* Returns an LDAP API code (LDAP_SUCCESS) if all goes well. +*/ +int LDAP_CALL +prldap_import_connection (LDAP *ld) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + int shared = 1; /* Assume shared init */ + LBER_SOCKET orig_socket = -1; + PRLDAPIOSessionArg *prsessp = NULL; + PRLDAPIOSocketArg *prsockp = NULL; + PRFileDesc *pr_socket = NULL; + + /* Check for invalid ld handle */ + if ( ld == NULL) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + /* Retrieve TCP socket's integer file descriptor */ + if ( ldap_get_option( ld, LDAP_OPT_DESC, &orig_socket ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + /* Check for NSPR functions on ld */ + if ( prldap_is_installed(ld)) { /* Error : NSPR already Installed */ + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + if (LDAP_SUCCESS != (rc = prldap_install_routines(ld, shared))) { + return( rc ); + } + + if (LDAP_SUCCESS != (rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + + /* Get NSPR Socket Arg */ + if ( NULL == ( prsockp = prldap_socket_arg_alloc( prsessp ))) { + ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + + /* Import file descriptor of connection made via ldap_init() */ + if (NULL == (pr_socket = PR_ImportTCPSocket(orig_socket)) ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + prsockp->prsock_prfd = pr_socket; + + /* Set Socket Arg in Extended I/O Layer */ + if ( ldap_set_option( ld, LDAP_X_OPT_SOCKETARG, prsockp) != 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + return( rc ); +} diff --git a/ldap/c-sdk/libraries/libprldap/ldappr-threads.c b/ldap/c-sdk/libraries/libprldap/ldappr-threads.c new file mode 100644 index 0000000000..1cfdc92635 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/ldappr-threads.c @@ -0,0 +1,643 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Thread callback functions for libldap that use the NSPR (Netscape + * Portable Runtime) thread API. + * + */ + +#include "ldappr-int.h" + +/* + * Macros: + */ +/* + * Grow thread private data arrays 10 elements at a time. + */ +#define PRLDAP_TPD_ARRAY_INCREMENT 10 + +/* + * Structures and types: + */ +/* + * Structure used by libldap thread callbacks to maintain error information. + */ +typedef struct prldap_errorinfo { + int plei_magic; /* must be first in the structure */ + int plei_lderrno; + char *plei_matched; + char *plei_errmsg; +} PRLDAP_ErrorInfo; + +#define PRLDAP_ERRORINFO_MAGIC 0x4D4F5A45 /* 'MOZE' */ + + +/* + * Structure used to maintain thread-private data. At the present time, + * only error info. is thread-private. One of these structures is allocated + * for each thread. + */ +typedef struct prldap_tpd_header { + int ptpdh_tpd_count; /* # of data items allocated */ + void **ptpdh_dataitems; /* array of data items */ +} PRLDAP_TPDHeader; + +/* + * Structure used by associate a PRLDAP thread-private data index with an + * LDAP session handle. One of these exists for each active LDAP session + * handle. + */ +typedef struct prldap_tpd_map { + LDAP *prtm_ld; /* non-NULL if in use */ + PRUintn prtm_index; /* index into TPD array */ + struct prldap_tpd_map *prtm_next; +} PRLDAP_TPDMap; + + + +/* + * Static Variables: + */ +/* + * prldap_map_list points to all of the PRLDAP_TPDMap structures + * we have ever allocated. We recycle them as we open and close LDAP + * sessions. + */ +static PRLDAP_TPDMap *prldap_map_list = NULL; + + +/* + * The prldap_map_mutex is used to protect access to the prldap_map_list. + */ +static PRLock *prldap_map_mutex = NULL; + +/* + * The prldap_tpd_maxindex value is used to track the largest TPD array + * index we have used. + */ +static PRInt32 prldap_tpd_maxindex = -1; + +/* + * prldap_tpdindex is an NSPR thread private data index we use to + * maintain our own thread-private data. It is initialized inside + * prldap_init_tpd(). + */ +static PRUintn prldap_tpdindex = 0; + +/* + * The prldap_callonce_init_tpd structure is used by NSPR to ensure + * that prldap_init_tpd() is called at most once. + */ +static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 }; + + +/* + * Private function prototypes: + */ +static void prldap_set_ld_error( int err, char *matched, char *errmsg, + void *errorarg ); +static int prldap_get_ld_error( char **matchedp, char **errmsgp, + void *errorarg ); +static void *prldap_mutex_alloc( void ); +static void prldap_mutex_free( void *mutex ); +static int prldap_mutex_lock( void *mutex ); +static int prldap_mutex_unlock( void *mutex ); +static void *prldap_get_thread_id( void ); +static PRStatus prldap_init_tpd( void ); +static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld ); +static void prldap_return_map( PRLDAP_TPDMap *map ); +static PRUintn prldap_new_tpdindex( void ); +static int prldap_set_thread_private( PRInt32 tpdindex, void *priv ); +static void *prldap_get_thread_private( PRInt32 tpdindex ); +static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, + int maxindex ); +static void prldap_tsd_destroy( void *priv ); + + +/* + * Install NSPR thread functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_thread_functions( LDAP *ld, int shared ) +{ + struct ldap_thread_fns tfns; + struct ldap_extra_thread_fns xtfns; + + if ( PR_CallOnce( &prldap_callonce_init_tpd, prldap_init_tpd ) + != PR_SUCCESS ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( -1 ); + } + + /* set thread function pointers */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_get_errno = prldap_get_system_errno; + tfns.ltf_set_errno = prldap_set_system_errno; + if ( shared ) { + tfns.ltf_mutex_alloc = prldap_mutex_alloc; + tfns.ltf_mutex_free = prldap_mutex_free; + tfns.ltf_mutex_lock = prldap_mutex_lock; + tfns.ltf_mutex_unlock = prldap_mutex_unlock; + tfns.ltf_get_lderrno = prldap_get_ld_error; + tfns.ltf_set_lderrno = prldap_set_ld_error; + if ( ld != NULL ) { + /* + * If this is a real ld (i.e., we are not setting the global + * defaults) allocate thread private data for error information. + * If ld is NULL we do not do this here but it is done in + * prldap_thread_new_handle(). + */ + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) + == NULL ) { + return( -1 ); + } + } + } + + if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); + return( -1 ); + } + + /* set extended thread function pointers */ + memset( &xtfns, '\0', sizeof(struct ldap_extra_thread_fns) ); + xtfns.ltf_threadid_fn = prldap_get_thread_id; + if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, + (void *)&xtfns ) != 0 ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_mutex_alloc( void ) +{ + return( (void *)PR_NewLock()); +} + + +static void +prldap_mutex_free( void *mutex ) +{ + PR_DestroyLock( (PRLock *)mutex ); +} + + +static int +prldap_mutex_lock( void *mutex ) +{ + PR_Lock( (PRLock *)mutex ); + return( 0 ); +} + + +static int +prldap_mutex_unlock( void *mutex ) +{ + if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_get_thread_id( void ) +{ + return( (void *)PR_GetCurrentThread()); +} + + +static int +prldap_get_ld_error( char **matchedp, char **errmsgp, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL && ( eip = + (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL ) { + if ( matchedp != NULL ) { + *matchedp = eip->plei_matched; + } + if ( errmsgp != NULL ) { + *errmsgp = eip->plei_errmsg; + } + return( eip->plei_lderrno ); + } else { + if ( matchedp != NULL ) { + *matchedp = NULL; + } + if ( errmsgp != NULL ) { + *errmsgp = NULL; + } + return( LDAP_LOCAL_ERROR ); /* punt */ + } +} + + +static void +prldap_set_ld_error( int err, char *matched, char *errmsg, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL ) { + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) == NULL ) { + /* + * Error info. has not yet been allocated for this thread. + * Do so now. Note that we free this memory only for the + * thread that calls prldap_thread_dispose_handle(), which + * should be the one that called ldap_unbind() -- see + * prldap_return_map(). Not freeing the memory used by + * other threads is deemed acceptable since it will be + * recycled and used by other LDAP sessions. All of the + * thread-private memory is freed when a thread exits + * (inside the prldap_tsd_destroy() function). + */ + eip = (PRLDAP_ErrorInfo *)PR_Calloc( 1, + sizeof( PRLDAP_ErrorInfo )); + if ( eip == NULL ) { + return; /* punt */ + } + eip->plei_magic = PRLDAP_ERRORINFO_MAGIC; + (void)prldap_set_thread_private( map->prtm_index, eip ); + } + + eip->plei_lderrno = err; + + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + eip->plei_matched = matched; + + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + eip->plei_errmsg = errmsg; + } +} + + +/* + * Utility function to free a PRLDAP_ErrorInfo structure and everything + * it contains. + */ +static void +prldap_free_errorinfo( PRLDAP_ErrorInfo *eip ) +{ + if ( NULL != eip && PRLDAP_ERRORINFO_MAGIC == eip->plei_magic ) { + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + + PR_Free( eip ); + } +} + + +/* + * Called when a new LDAP * session handle is allocated. + * Allocate thread-private data for error information, but only if + * it has not already been allocated and the get_ld_error callback has + * been installed. If ld is not NULL when prldap_install_thread_functions() + * is called, we will have already allocated the thread-private data there. + */ +int +prldap_thread_new_handle( LDAP *ld, void *sessionarg ) +{ + struct ldap_thread_fns tfns; + + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + + if ( tfns.ltf_lderrno_arg == NULL && tfns.ltf_get_lderrno != NULL ) { + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) == NULL + || ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + } + + return( LDAP_SUCCESS ); +} + + +/* + * Called when an LDAP * session handle is being destroyed. + * Clean up our thread private data map. + */ +void +prldap_thread_dispose_handle( LDAP *ld, void *sessionarg ) +{ + struct ldap_thread_fns tfns; + + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) == 0 && + tfns.ltf_lderrno_arg != NULL ) { + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); + } +} + + +static PRStatus +prldap_init_tpd( void ) +{ + if (( prldap_map_mutex = PR_NewLock()) == NULL || PR_NewThreadPrivateIndex( + &prldap_tpdindex, prldap_tsd_destroy ) != PR_SUCCESS ) { + return( PR_FAILURE ); + } + + prldap_map_list = NULL; + + return( PR_SUCCESS ); +} + + +/* + * Function: prldap_allocate_map() + * Description: allocate a thread-private data map to use for a new + * LDAP session handle. + * Returns: a pointer to the TPD map or NULL if none available. + */ +static PRLDAP_TPDMap * +prldap_allocate_map( LDAP *ld ) +{ + PRLDAP_TPDMap *map, *prevmap; + + PR_Lock( prldap_map_mutex ); + + /* + * first look for a map that is already allocated but free to be re-used + */ + prevmap = NULL; + for ( map = prldap_map_list; map != NULL; map = map->prtm_next ) { + if ( map->prtm_ld == NULL ) { + break; + } + prevmap = map; + } + + /* + * if none was found (map == NULL), try to allocate a new one and add it + * to the end of our global list. + */ + if ( map == NULL ) { + PRUintn tpdindex; + + tpdindex = prldap_new_tpdindex(); + map = (PRLDAP_TPDMap *)PR_Malloc( sizeof( PRLDAP_TPDMap )); + if ( map != NULL ) { + map->prtm_index = tpdindex; + map->prtm_next = NULL; + if ( prevmap == NULL ) { + prldap_map_list = map; + } else { + prevmap->prtm_next = map; + } + } + } + + if ( map != NULL ) { + map->prtm_ld = ld; /* now marked as "in use" */ + + /* + * If old thread-private error information exists, reset it. It may + * have been left behind by an old LDAP session that was used by + * this thread but disposed of by a different thread. + */ + if ( NULL != prldap_get_thread_private( map->prtm_index )) { + prldap_set_ld_error( LDAP_SUCCESS, NULL, NULL, map ); + } + } + + PR_Unlock( prldap_map_mutex ); + + return( map ); +} + + +/* + * Function: prldap_return_map() + * Description: return a thread-private data map to the pool of ones + * available for re-use. + */ +static void +prldap_return_map( PRLDAP_TPDMap *map ) +{ + PRLDAP_ErrorInfo *eip; + + PR_Lock( prldap_map_mutex ); + + /* + * Dispose of thread-private LDAP error information. Note that this + * only disposes of the memory consumed on THIS thread, but that is + * okay. See the comment in prldap_set_ld_error() for the reason why. + */ + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL && + prldap_set_thread_private( map->prtm_index, NULL ) == 0 ) { + prldap_free_errorinfo( eip ); + } + + /* mark map as available for re-use */ + map->prtm_ld = NULL; + + PR_Unlock( prldap_map_mutex ); +} + + +/* + * Function: prldap_new_tpdindex() + * Description: allocate a thread-private data index. + * Returns: the new index. + */ +static PRUintn +prldap_new_tpdindex( void ) +{ + PRUintn tpdindex; + + tpdindex = (PRUintn)PR_AtomicIncrement( &prldap_tpd_maxindex ); + return( tpdindex ); +} + + +/* + * Function: prldap_set_thread_private() + * Description: store a piece of thread-private data. + * Returns: 0 if successful and -1 if not. + */ +static int +prldap_set_thread_private( PRInt32 tpdindex, void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + + if ( tpdindex > prldap_tpd_maxindex ) { + return( -1 ); /* bad index */ + } + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL || tpdindex >= tsdhdr->ptpdh_tpd_count ) { + tsdhdr = prldap_tsd_realloc( tsdhdr, tpdindex ); + if ( tsdhdr == NULL ) { + return( -1 ); /* realloc failed */ + } + } + + tsdhdr->ptpdh_dataitems[ tpdindex ] = priv; + return( 0 ); +} + + +/* + * Function: prldap_get_thread_private() + * Description: retrieve a piece of thread-private data. If not set, + * NULL is returned. + * Returns: 0 if successful and -1 if not. + */ +static void * +prldap_get_thread_private( PRInt32 tpdindex ) +{ + PRLDAP_TPDHeader *tsdhdr; + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL ) { + return( NULL ); /* no thread private data */ + } + + if ( tpdindex >= tsdhdr->ptpdh_tpd_count + || tsdhdr->ptpdh_dataitems == NULL ) { + return( NULL ); /* fewer data items than requested index */ + } + + return( tsdhdr->ptpdh_dataitems[ tpdindex ] ); +} + + +/* + * Function: prldap_tsd_realloc() + * Description: enlarge the thread-private data array. + * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful). + * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader). + */ +static PRLDAP_TPDHeader * +prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, int maxindex ) +{ + void *newdataitems = NULL; + int count; + + if ( tsdhdr == NULL ) { + /* allocate a new thread private data header */ + if (( tsdhdr = PR_Calloc( 1, sizeof( PRLDAP_TPDHeader ))) == NULL ) { + return( NULL ); + } + (void)PR_SetThreadPrivate( prldap_tpdindex, tsdhdr ); + } + + /* + * Make the size of the new array the next highest multiple of + * the array increment value that is greater than maxindex. + */ + count = PRLDAP_TPD_ARRAY_INCREMENT * + ( 1 + ( maxindex / PRLDAP_TPD_ARRAY_INCREMENT )); + + /* increase the size of the data item array if necessary */ + if ( count > tsdhdr->ptpdh_tpd_count ) { + newdataitems = (PRLDAP_ErrorInfo *)PR_Calloc( count, sizeof( void * )); + if ( newdataitems == NULL ) { + return( NULL ); + } + if ( tsdhdr->ptpdh_dataitems != NULL ) { /* preserve old data */ + memcpy( newdataitems, tsdhdr->ptpdh_dataitems, + tsdhdr->ptpdh_tpd_count * sizeof( void * )); + PR_Free( tsdhdr->ptpdh_dataitems ); + } + + tsdhdr->ptpdh_tpd_count = count; + tsdhdr->ptpdh_dataitems = newdataitems; + } + + return( tsdhdr ); +} + + +/* + * Function: prldap_tsd_destroy() + * Description: Free a thread-private data array. Installed as an NSPR TPD + * destructor function + * Returns: nothing. + */ +static void +prldap_tsd_destroy( void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + PRLDAP_ErrorInfo *eip; + int i; + + tsdhdr = (PRLDAP_TPDHeader *)priv; + if ( tsdhdr != NULL ) { + if ( tsdhdr->ptpdh_dataitems != NULL ) { + for ( i = 0; i < tsdhdr->ptpdh_tpd_count; ++i ) { + if ( tsdhdr->ptpdh_dataitems[ i ] != NULL ) { + eip = (PRLDAP_ErrorInfo *)tsdhdr->ptpdh_dataitems[ i ]; + if ( PRLDAP_ERRORINFO_MAGIC == eip->plei_magic ) { + prldap_free_errorinfo( eip ); + } else { + PR_Free( tsdhdr->ptpdh_dataitems[ i ] ); + } + tsdhdr->ptpdh_dataitems[ i ] = NULL; + } + } + PR_Free( tsdhdr->ptpdh_dataitems ); + tsdhdr->ptpdh_dataitems = NULL; + } + PR_Free( tsdhdr ); + } +} diff --git a/ldap/c-sdk/libraries/libprldap/libprldap.def b/ldap/c-sdk/libraries/libprldap/libprldap.def new file mode 100644 index 0000000000..a18e7ee824 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/libprldap.def @@ -0,0 +1,59 @@ +; +; ***** BEGIN LICENSE BLOCK ***** +; Version: MPL 1.1/GPL 2.0/LGPL 2.1 +; +; The contents of this file are subject to the Mozilla Public License Version +; 1.1 (the "License"); you may not use this file except in compliance with +; the License. You may obtain a copy of the License at +; http://www.mozilla.org/MPL/ +; +; Software distributed under the License is distributed on an "AS IS" basis, +; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +; for the specific language governing rights and limitations under the +; License. +; +; The Original Code is Mozilla Communicator client code. +; +; The Initial Developer of the Original Code is +; Netscape Communications Corporation. +; Portions created by the Initial Developer are Copyright (C) 1996-1999 +; the Initial Developer. All Rights Reserved. +; +; Contributor(s): +; +; Alternatively, the contents of this file may be used under the terms of +; either of the GNU General Public License Version 2 or later (the "GPL"), +; or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +; in which case the provisions of the GPL or the LGPL are applicable instead +; of those above. If you wish to allow use of your version of this file only +; under the terms of either the GPL or the LGPL, and not to allow others to +; use your version of this file under the terms of the MPL, indicate your +; decision by deleting the provisions above and replace them with the notice +; and other provisions required by the GPL or the LGPL. If you do not delete +; the provisions above, a recipient may use your version of this file under +; the terms of any one of the MPL, the GPL or the LGPL. +; +; ***** END LICENSE BLOCK ***** + +LIBRARY PRLDAP60 +VERSION 6.0 +HEAPSIZE 4096 + +EXPORTS +; exports list (generated by genexports.pl) +; + + prldap_init + prldap_install_routines + prldap_set_session_info + prldap_get_session_info + prldap_set_socket_info + prldap_get_socket_info + prldap_set_session_option + prldap_get_session_option + prldap_is_installed + prldap_import_connection + prldap_set_default_socket_info + prldap_get_default_socket_info +; +; end of generated exports list. diff --git a/ldap/c-sdk/libraries/libprldap/moz.build b/ldap/c-sdk/libraries/libprldap/moz.build new file mode 100644 index 0000000000..422557cc95 --- /dev/null +++ b/ldap/c-sdk/libraries/libprldap/moz.build @@ -0,0 +1,31 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include('/ldap/ldap-sdk.mozbuild') + +SharedLibrary('prldap60') + +SOURCES += [ + 'ldappr-dns.c', + 'ldappr-error.c', + 'ldappr-io.c', + 'ldappr-public.c', + 'ldappr-threads.c', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + DEFFILE = SRCDIR + '/libprldap.def' + +DEFINES['USE_WAITPID'] = True +DEFINES['NEEDPROTOS'] = True + +LOCAL_INCLUDES += [ + '/ldap/c-sdk/include' +] + +USE_LIBS += [ + 'ldap60', + 'nspr' +] diff --git a/ldap/c-sdk/libraries/moz.build b/ldap/c-sdk/libraries/moz.build new file mode 100644 index 0000000000..339783bfd7 --- /dev/null +++ b/ldap/c-sdk/libraries/moz.build @@ -0,0 +1,11 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += [ + 'liblber', + 'libldif', + 'libldap', + 'libprldap' +] diff --git a/ldap/ldap-sdk.mozbuild b/ldap/ldap-sdk.mozbuild new file mode 100644 index 0000000000..4377982c5a --- /dev/null +++ b/ldap/ldap-sdk.mozbuild @@ -0,0 +1,26 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +NO_VISIBILITY_FLAGS = True +DISABLE_STL_WRAPPING = True +ALLOW_COMPILER_WARNINGS = True + +if CONFIG['OS_TARGET'] == 'Linux': + DEFINES['LINUX'] = 1 + DEFINES['LINUX2_0'] = True + DEFINES['linux'] = 1 +elif CONFIG['OS_TARGET'] == 'Darwin': + DEFINES["DARWIN"] = 1 +elif CONFIG['OS_TARGET'] in ('OpenBSD', 'FreeBSD', 'NetBSD'): + DEFINES[CONFIG['OS_TARGET'].upper()] = True +elif CONFIG['OS_ARCH'] == 'WINNT': + DEFINES['_WINDOWS'] = True + +DEFINES['_PR_PTHREADS'] = True +DEFINES['NET_SSL'] = True +DEFINES['NS_DOMESTIC'] = True + +if CONFIG['MOZ_DEBUG']: + DEFINES['LDAP_DEBUG'] = True diff --git a/ldap/moz.build b/ldap/moz.build new file mode 100644 index 0000000000..cd511ceb1e --- /dev/null +++ b/ldap/moz.build @@ -0,0 +1,16 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Library('ldapsdks') + +DIRS += [ + 'c-sdk/libraries' +] + +USE_LIBS += [ + 'ldap60', + 'ldif60', + 'prldap60', +] diff --git a/ldap/xpcom/README.txt b/ldap/xpcom/README.txt new file mode 100644 index 0000000000..d621b14fc3 --- /dev/null +++ b/ldap/xpcom/README.txt @@ -0,0 +1,24 @@ +This is the home for XPCOM modules that implement LDAP functionality. + +What's Here +----------- +base/ + Implements a wrapper around the LDAP C SDK, as well as support + for ldap: URLs in the browser. Written entirely in C++; + theoretically only depends on necko, xpcom, nspr, and the LDAP + C SDK. + +datasource/ + An RDF datasource, written in Javascript. + +tests/ + Some basic tests to help ensure that things don't break as + development proceeds. Currently, there is only some stuff for + testing the datasource. + + +Building +-------- +See . + +Dan Mosedale diff --git a/ldap/xpcom/TODO.txt b/ldap/xpcom/TODO.txt new file mode 100644 index 0000000000..ff70c2bcda --- /dev/null +++ b/ldap/xpcom/TODO.txt @@ -0,0 +1,155 @@ +housecleaning for first 0.x release +----------------------------------- +* error handling: sort out what should be NS_ASSERTIONs vs. normal + error conditions (eg network & data errors), especially in nsLDAPChannel. + also shouldn't be casting results of nsILDAPConnection::GetErrorString to + void in ldapSearch. (ideally this would use xpidl nsresult decls + (bug 13423), but that's not done yet). This also requires some + threadsafety work related to the data and interfaces touched by + nsLDAPChannel::Cancel, since these are used in the connection + callbacks, and we also need to be able to cancel from the connection + callbacks themselves. + +* audit for and fix leaks / bloat + +* destroy connection & thread when done with it; deal with timeouts + (blocked on LDAP C SDK 4.1, in the hopes that the NSPR + binding of the IO functions will provide us with some way to pop out + of the select() that's called under ldap_result(). It may not be worth + implementing this until nsLDAPService is resurrected. If this + doesn't work, we may have to give nsLDAPConnection an event queue & + loop of its own, instead of using the LDAP C SDK as a vector to get + events over there. + +items blocked waiting for other work +------------------------------------ +* go through the various options that can be used with the SDK function + ldap_set_options() and decide if and where the various functions should + be used. This will include memory allocator settings, and possibly + threadsafety callbacks (if necessary). (blocked waiting on LDAP C + SDK 4.1 to land, since it contains NSPR bindings for some of this). + +* searches that return lots of entries to the nsLDAPChannel + (eg (sn=Smith) at netcenter) mostly stall out the UI. moving most of the + callback work off the UI thread to the LDAP connection thread didn't + help; so this seems to be lossage in the event system itself (bug 50104). + +* deal with race condition that happens if data comes back after + nsLDAPChannel::Cancel is called. AsyncStreamListener expects + GetStatus to return OK, and asserts when it doesn't. (blocked waiting + on insight from Warren about nsSocketTransport's use of mCancelStatus). + +* ensure that multiple calls to Cancel don't do the wrong thing + +* move the ldap_unbind() call out of the nsLDAPConnection destructor, + since nsLDAPConnection will soon go back to being callable from the + UI thread, and ldap_unbind() is actually synchronous. additionally, + arrange for the connection thread to shutdown. (waiting for + nsILDAPService to be implemented on the theory that nsILDAPService + might have it's own thread and it would be reasonable to call + ldap_unbind() from that thread). + +* currently nsILDAPOperation::SimpleBind is called as though it were + asynchronous (ie from the UI thread), which it mostly is -- the call + to connect() can stall. We could use the ASYNC_CONNECT flag, but it + seems to do weird stuff on Linux, and mcs says it isn't well tested + anyway. At the moment, my feeling is that fixing ASYNC_CONNECT is + probably the right thing to do, but the bind could actually be + pushed from nsILDAPOperation into nsILDAPConnection and then called + after the thread for the connection is spun up (waiting on help from + the LDAP SDK folks: bug 50074). + +misc +---- +* verify that ldap_parse_result() and ldap_get_ldaperrno() aren't + required to be called from the same thread as ldap_result() and + before the thread-specific ldaperrno has a chance to change. Note: + now that ldap_parse_result is only called inside the initializer + nsLDAPMessage::Init(), this is probably no longer an issue there at + least. Need to confirm. + +* replace instances of (obsolete) |static NS_DEFINE_IID| variables + with direct calls to NS_GET_IID in the code. + +* investigate use of DNS in LDAP sdk. I think sync functions used in the + wrong places (eg they end up getting called from Mozilla on the UI thread)? + +* audit for and implement any appropriate NOT_IMPLEMENTED functions + +* re-read the IDL for interfaces implemented by nsLDAPChannel.cpp make sure + all interfaces are implemented correctly + +* implement progress info interfaces, if possible (% done) + +* investigate the MOZILLA_CLIENT define as used by the SDK. eg do we still + want the reference to "xp_sort.h"? + +* i18n / l10n (any text strings) + +* cachability of nsILDAPChannel content: browser cache? ldap_memcache? both? + +* use a rebind_proc? + +* HTMLize nsLDAPChannel output for linkification +* the LDAP SDK returns UTF8, but I think nsILDAPChannel is handing it + off to callers as ASCII. How does this all relate to the stated + UCS2 policy in Mozilla? + +* all attributes are assumed to be strings right now. This probably + needs to change: assume all attributes are binary, use some + heuristic to figure out if they're a string. I wonder how + ldapsearch does this. + +* grep for XXXs and fix the issues + +rdf datasource +-------------- + +* revamp nsILDAPService (currently unused code) to manage LDAP + connections and allow for sharing connections between browser + clients. I think this should obviate the need to hold onto the + connection with a delegate factory. + +* non-anonymous binding (ie nsLDAPURL supports x-bind-name -- I + suspect this may come with the LDAP C SDK version after 4.1, + though.) + +testing +------- +* see how the browser copes when it does such a big search that the server + only returns partial results and an error. + +perf +---- +* rather than having one thread per nsILDAPConnection, have multiple + nsILDAPConnections share the same thread. possibly pre-spin up a + thread-pool to handle this? + +* nsLDAPService: assuming that we do the above and start using + nsLDAPService again, need to implement shutdown for nsLDAPService + (probably analogous to the way nsSocketTransportService shuts down. + but how does nsIShutdownListener fit in here? and CanUnload?) + +* remove any unnecessary thread-safety code (NS_IMPL_THREADSAFE) to avoid + locking overhead. need to figure out if everything must be + threadsafe or not. + +later +----- +* rationalize the logging strategy. right now there is a mix of + PR_LOG/NS{WARNING|ERROR} and nsIConsoleService and non-logging, + as I have been vacillating on how much logging it's really important to have + (part of my thought process about the XPCOM-wrapper being part of + Mozilla-as-platform) +* get rid of inappropriate use of nsVoidKey by implementing an nsPRInt32Key +* handle referrals +* failover +* addressbook/mail UI glue +* PSM certs from directory glue(?) +* secure (& proxied/socksified?) ldap +* make the LDAP C SDK autoconf glue not be a shim and not require + nsprpub build infrastructure. requires work with the LDAP C SDK + owners, and shouldn't this shouldn't happen until after the most + current ldap SDK code lands in mozilla.org anyway. +* figure out our strategy for LDAPv2 vs. LDAPv3. right now, the code just + uses the C SDK's default of always advertising itself as LDAPv2. diff --git a/ldap/xpcom/moz.build b/ldap/xpcom/moz.build new file mode 100644 index 0000000000..27dcb87465 --- /dev/null +++ b/ldap/xpcom/moz.build @@ -0,0 +1,9 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += [ + 'public', + 'src', +] diff --git a/ldap/xpcom/public/moz.build b/ldap/xpcom/public/moz.build new file mode 100644 index 0000000000..9ff6b21c68 --- /dev/null +++ b/ldap/xpcom/public/moz.build @@ -0,0 +1,27 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + 'nsILDAPBERElement.idl', + 'nsILDAPBERValue.idl', + 'nsILDAPConnection.idl', + 'nsILDAPControl.idl', + 'nsILDAPErrors.idl', + 'nsILDAPMessage.idl', + 'nsILDAPMessageListener.idl', + 'nsILDAPModification.idl', + 'nsILDAPOperation.idl', + 'nsILDAPServer.idl', + 'nsILDAPService.idl', + 'nsILDAPURL.idl', +] + +if CONFIG['MOZ_PREF_EXTENSIONS']: + XPIDL_SOURCES += [ + 'nsILDAPSyncQuery.idl', + ] + +XPIDL_MODULE = 'mozldap' + diff --git a/ldap/xpcom/public/nsILDAPBERElement.idl b/ldap/xpcom/public/nsILDAPBERElement.idl new file mode 100644 index 0000000000..24a662782a --- /dev/null +++ b/ldap/xpcom/public/nsILDAPBERElement.idl @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPBERValue; + + +/** + * nsILDAPBERElement is a wrapper interface for a C-SDK BerElement object. + * Typically, this is used as an intermediate object to aid in the manual + * construction of a BER value. Once the construction is completed by calling + * methods on this object, an nsILDAPBERValue can be retrieved from the + * asValue attribute on this interface. + * + * + * contains some documentation that mostly (but not exactly) matches + * the code that this wraps in section 17. + */ + +[scriptable, uuid(409f5b31-c062-4d11-a35b-0a09e7967bf2)] +interface nsILDAPBERElement : nsISupports +{ + /** + * Initialize this object. Must be called before calling any other method + * on this interface. + * + * @param aValue value to preinitialize with; 0 for a new empty object + * + * @exception NS_ERROR_NOT_IMPLEMENTED preinitialization is currently + * not implemented + * @exception NS_ERROR_OUT_OF_MEMORY unable to allocate the internal + * BerElement + */ + void init(in nsILDAPBERValue aValue); + + /** + * Most TAG_* constants can be used in the construction or passing in of + * values to the aTag arguments to most of the methods in this interface. + */ + + /** + * When returned from a parsing method, 0xffffffff is referred to + * has the parse-error semantic (ie TAG_LBER_ERROR); when passing it to + * a construction method, it is used to mean "pick the default tag for + * this type" (ie TAG_LBER_DEFAULT). + */ + const unsigned long TAG_LBER_ERROR = 0xffffffff; + const unsigned long TAG_LBER_DEFAULT = 0xffffffff; + const unsigned long TAG_LBER_END_OF_SEQORSET = 0xfffffffe; + + /** + * BER encoding types and masks + */ + const unsigned long TAG_LBER_PRIMITIVE = 0x00; + + /** + * The following two tags are carried over from the LDAP C SDK; their + * exact purpose there is not well documented. They both have + * the same value there as well. + */ + const unsigned long TAG_LBER_CONSTRUCTED = 0x20; + const unsigned long TAG_LBER_ENCODING_MASK = 0x20; + + const unsigned long TAG_LBER_BIG_TAG_MASK = 0x1f; + const unsigned long TAG_LBER_MORE_TAG_MASK = 0x80; + + /** + * general BER types we know about + */ + const unsigned long TAG_LBER_BOOLEAN = 0x01; + const unsigned long TAG_LBER_INTEGER = 0x02; + const unsigned long TAG_LBER_BITSTRING = 0x03; + const unsigned long TAG_LBER_OCTETSTRING = 0x04; + const unsigned long TAG_LBER_NULL = 0x05; + const unsigned long TAG_LBER_ENUMERATED = 0x0a; + const unsigned long TAG_LBER_SEQUENCE = 0x30; + const unsigned long TAG_LBER_SET = 0x31; + + /** + * Write a string to this element. + * + * @param aString string to write + * @param aTag tag for this string (if TAG_LBER_DEFAULT is used, + * TAG_LBER_OCTETSTRING will be written). + * + * @return number of bytes written + * + * @exception NS_ERROR_FAILUE C-SDK returned error + */ + unsigned long putString(in AUTF8String aString, in unsigned long aTag); + + /** + * Start a set. Sets may be nested. + * + * @param aTag tag for this set (if TAG_LBER_DEFAULT is used, + * TAG_LBER_SET will be written). + * + * @exception NS_ERROR_FAILUE C-SDK returned an error + */ + void startSet(in unsigned long aTag); + + /** + * Cause the entire set started by the last startSet() call to be written. + * + * @exception NS_ERROR_FAILUE C-SDK returned an error + * + * @return number of bytes written + */ + unsigned long putSet(); + + /** + * an nsILDAPBERValue version of this element. Calls ber_flatten() under + * the hood. + * + * @exception NS_ERROR_OUT_OF_MEMORY + */ + readonly attribute nsILDAPBERValue asValue; +}; diff --git a/ldap/xpcom/public/nsILDAPBERValue.idl b/ldap/xpcom/public/nsILDAPBERValue.idl new file mode 100644 index 0000000000..da918d6392 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPBERValue.idl @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * Representation of a BER value as an interface containing an array of + * bytes. Someday this should perhaps be obsoleted by a better, more + * generalized version of nsIByteBuffer, but that's currently not even + * scriptable (see bug 125596). + */ +[scriptable, uuid(c817c5fe-1dd1-11b2-a10b-ae9885762ea9)] +interface nsILDAPBERValue : nsISupports +{ + /** + * Set the BER value from an array of bytes (copies). + * + * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to + */ + void set(in unsigned long aCount, + [array, size_is(aCount)] in octet aValue); + + /** + * Set the BER value from a UTF8 string (copies). + * + * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to + */ + void setFromUTF8(in AUTF8String aValue); + + /** + * Get the BER value as an array of bytes. Note that if this value is + * zero-length, aCount and aRetVal will both be 0. This means that + * (in C++ anyway) the caller MUST test either aCount or aRetval before + * dereferencing aRetVal. + * + * @exception NS_ERROR_OUT_OF_MEMORY couldn't allocate buffer to copy to + */ + void get(out unsigned long aCount, + [retval, array, size_is(aCount)] out octet aRetVal); +}; + diff --git a/ldap/xpcom/public/nsILDAPConnection.idl b/ldap/xpcom/public/nsILDAPConnection.idl new file mode 100644 index 0000000000..63b3673637 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPConnection.idl @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPOperation; +interface nsILDAPMessageListener; +interface nsILDAPURL; + +%{C++ +#define NS_LDAPCONNECTION_CONTRACTID "@mozilla.org/network/ldap-connection;1" +%} + +[scriptable, uuid(360c1ff7-15e3-4ffe-b4b8-0eda72ebc096)] +interface nsILDAPConnection : nsISupports +{ + /** + * the string version of lderrno + */ + readonly attribute wstring errorString; + + /** + * DN to bind as. use the init() method to set this. + * + * @exception NS_ERROR_OUT_OF_MEMORY + */ + readonly attribute AUTF8String bindName; + + /** + * private parameter (anything caller desires) + */ + attribute nsISupports closure; + + /** + * Set up the connection. Note that init() must be called on a thread + * that already has an nsIEventQueue. + * + * @param aUrl A URL for the ldap server. The host, port and + * ssl connection type will be extracted from this + * @param aBindName DN to bind as + * @param aMessageListener Callback for DNS resolution completion + * @param aClosure private parameter (anything caller desires) + * @param aVersion LDAP version to use (currently VERSION2 or + * VERSION3) + * + * @exception NS_ERROR_ILLEGAL_VALUE null pointer or invalid version + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_OFFLINE we are in off-line mode + * @exception NS_ERROR_FAILURE + * @exception NS_ERROR_UNEXPECTED internal error + */ + void init(in nsILDAPURL aUrl, + in AUTF8String aBindName, + in nsILDAPMessageListener aMessageListener, + in nsISupports aClosure, in unsigned long aVersion); + + const unsigned long VERSION2 = 2; + const unsigned long VERSION3 = 3; + + /** + * Get information about the last error that occured on this connection. + * + * @param matched if the server is returning LDAP_NO_SUCH_OBJECT, + * LDAP_ALIAS_PROBLEM, LDAP_INVALID_DN_SYNTAX, + * or LDAP_ALIAS_DEREF_PROBLEM, this will contain + * the portion of DN that matches the entry that is + * closest to the requested entry + * + * @param s additional error information from the server + * + * @return the error code, as defined in nsILDAPErrors.idl + */ + long getLdErrno(out AUTF8String matched, out AUTF8String s); +}; diff --git a/ldap/xpcom/public/nsILDAPControl.idl b/ldap/xpcom/public/nsILDAPControl.idl new file mode 100644 index 0000000000..89b87f8503 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPControl.idl @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPBERValue; + +/** + * XPCOM representation of the C SDK LDAPControl structure. + */ +[scriptable, uuid(3a7ceb8e-482a-4a4f-9aa4-26b9a69a3595)] +interface nsILDAPControl : nsISupports +{ + /** + * Control type, represented as a string. + * + * @exceptions none + */ + attribute ACString oid; + + /** + * The data associated with a control, if any. To specify that no data + * is to be associated with the control, don't set this at all (which + * is equivalent to setting it to null). + * + * @note Specifying a zero-length value is not currently supported. At some + * date, setting this to an nsILDAPBERValue which has not had any of the + * set methods called will be the appropriate way to do that. + * + * @exceptions none + */ + attribute nsILDAPBERValue value; + + /** + * Should the client or server abort if the control is not understood? + * Should be set to false for server controls used in abandon and unbind + * operations, since those have no server response. + * + * @exceptions none + */ + attribute boolean isCritical; +}; diff --git a/ldap/xpcom/public/nsILDAPErrors.idl b/ldap/xpcom/public/nsILDAPErrors.idl new file mode 100644 index 0000000000..f85f75d792 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPErrors.idl @@ -0,0 +1,447 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * Error codes used in the LDAP XPCOM SDK. + * + * Taken from the Mozilla C SDK's ldap.h include file, these should be + * the same as those specified in the draft-ietf-ldapext-ldap-c-api-04.txt + * Internet Draft. + * + * The only good documentation I'm aware of for these error codes is + * at . + * Unfortunately, this does not currently seem to be available under any + * open source license, so I can't include that documentation here as + * doxygen comments. + * + */ +[scriptable, uuid(f9ac10fa-1dd1-11b2-9798-8d5cbda95d74)] +interface nsILDAPErrors : nsISupports +{ + + const long SUCCESS = 0x00; + + const long OPERATIONS_ERROR = 0x01; + + const long PROTOCOL_ERROR = 0x02; + + const long TIMELIMIT_EXCEEDED = 0x03; + + const long SIZELIMIT_EXCEEDED = 0x04; + + const long COMPARE_FALSE = 0x05; + + const long COMPARE_TRUE = 0x06; + + const long STRONG_AUTH_NOT_SUPPORTED = 0x07; + + const long STRONG_AUTH_REQUIRED = 0x08; + + + /** + * UMich LDAPv2 extension + */ + const long PARTIAL_RESULTS = 0x09; + + /** + * new in LDAPv3 + */ + const long REFERRAL = 0x0a; + + /** + * new in LDAPv3 + */ + const long ADMINLIMIT_EXCEEDED = 0x0b; + + /** + * new in LDAPv3 + */ + const long UNAVAILABLE_CRITICAL_EXTENSION = 0x0c; + + /** + * new in LDAPv3 + */ + const long CONFIDENTIALITY_REQUIRED = 0x0d; + + /** + * new in LDAPv3 + */ + const long SASL_BIND_IN_PROGRESS = 0x0e; + + const long NO_SUCH_ATTRIBUTE = 0x10; + + const long UNDEFINED_TYPE = 0x11; + + const long INAPPROPRIATE_MATCHING = 0x12; + + const long CONSTRAINT_VIOLATION = 0x13; + + const long TYPE_OR_VALUE_EXISTS = 0x14; + + const long INVALID_SYNTAX = 0x15; + + const long NO_SUCH_OBJECT = 0x20; + + const long ALIAS_PROBLEM = 0x21; + + const long INVALID_DN_SYNTAX = 0x22; + + /** + * not used in LDAPv3 + */ + const long IS_LEAF = 0x23; + + const long ALIAS_DEREF_PROBLEM = 0x24; + + const long INAPPROPRIATE_AUTH = 0x30; + + const long INVALID_CREDENTIALS = 0x31; + + const long INSUFFICIENT_ACCESS = 0x32; + + const long BUSY = 0x33; + + const long UNAVAILABLE = 0x34; + + const long UNWILLING_TO_PERFORM = 0x35; + + const long LOOP_DETECT = 0x36; + + /** + * server side sort extension + */ + const long SORT_CONTROL_MISSING = 0x3C; + + /** + * VLV extension + */ + const long INDEX_RANGE_ERROR = 0x3D; + + const long NAMING_VIOLATION = 0x40; + + const long OBJECT_CLASS_VIOLATION = 0x41; + + const long NOT_ALLOWED_ON_NONLEAF = 0x42; + + const long NOT_ALLOWED_ON_RDN = 0x43; + + const long ALREADY_EXISTS = 0x44; + + const long NO_OBJECT_CLASS_MODS = 0x45; + + /** + * reserved CLDAP + */ + const long RESULTS_TOO_LARGE = 0x46; + + /** + * new in LDAPv3 + */ + const long AFFECTS_MULTIPLE_DSAS = 0x47; + + const long OTHER = 0x50; + + const long SERVER_DOWN = 0x51; + + const long LOCAL_ERROR = 0x52; + + const long ENCODING_ERROR = 0x53; + + const long DECODING_ERROR = 0x54; + + const long TIMEOUT = 0x55; + + const long AUTH_UNKNOWN = 0x56; + + const long FILTER_ERROR = 0x57; + + const long USER_CANCELLED = 0x58; + + const long PARAM_ERROR = 0x59; + + const long NO_MEMORY = 0x5a; + + const long CONNECT_ERROR = 0x5b; + + /** + * new in LDAPv3 + */ + const long NOT_SUPPORTED = 0x5c; + + /** + * new in LDAPv3 + */ + const long CONTROL_NOT_FOUND = 0x5d; + + /** + * new in LDAPv3 + */ + const long NO_RESULTS_RETURNED = 0x5e; + + /** + * new in LDAPv3 + */ + const long MORE_RESULTS_TO_RETURN = 0x5f; + + /** + * new in LDAPv3 + */ + const long CLIENT_LOOP = 0x60; + + /** + * new in LDAPv3 + */ + const long REFERRAL_LIMIT_EXCEEDED = 0x61; +}; + +/* + * Map these errors codes into the nsresult namespace in C++ + */ +%{C++ + +#define NS_ERROR_LDAP_OPERATIONS_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::OPERATIONS_ERROR) + +#define NS_ERROR_LDAP_PROTOCOL_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::PROTOCOL_ERROR) + +#define NS_ERROR_LDAP_TIMELIMIT_EXCEEDED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::TIMELIMIT_EXCEEDED) + +#define NS_ERROR_LDAP_SIZELIMIT_EXCEEDED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::SIZELIMIT_EXCEEDED) + +#define NS_ERROR_LDAP_COMPARE_FALSE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::COMPARE_FALSE) + +#define NS_ERROR_LDAP_COMPARE_TRUE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::COMPARE_TRUE) + +#define NS_ERROR_LDAP_STRONG_AUTH_NOT_SUPPORTED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::STRONG_AUTH_NOT_SUPPORTED) + +#define NS_ERROR_LDAP_STRONG_AUTH_REQUIRED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::STRONG_AUTH_REQUIRED) + +#define NS_ERROR_LDAP_PARTIAL_RESULTS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::PARTIAL_RESULTS) + +#define NS_ERROR_LDAP_REFERRAL \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::REFERRAL) + +#define NS_ERROR_LDAP_ADMINLIMIT_EXCEEDED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::ADMINLIMIT_EXCEEDED) + +#define NS_ERROR_LDAP_UNAVAILABLE_CRITICAL_EXTENSION \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::UNAVAILABLE_CRITICAL_EXTENSION) + +#define NS_ERROR_LDAP_CONFIDENTIALITY_REQUIRED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::CONFIDENTIALITY_REQUIRED) + +#define NS_ERROR_LDAP_SASL_BIND_IN_PROGRESS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::SASL_BIND_IN_PROGRESS) + +#define NS_ERROR_LDAP_NO_SUCH_ATTRIBUTE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NO_SUCH_ATTRIBUTE) + +#define NS_ERROR_LDAP_UNDEFINED_TYPE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::UNDEFINED_TYPE) + +#define NS_ERROR_LDAP_INAPPROPRIATE_MATCHING \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INAPPROPRIATE_MATCHING) + +#define NS_ERROR_LDAP_CONSTRAINT_VIOLATION \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::CONSTRAINT_VIOLATION) + +#define NS_ERROR_LDAP_TYPE_OR_VALUE_EXISTS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::TYPE_OR_VALUE_EXISTS) + +#define NS_ERROR_LDAP_INVALID_SYNTAX \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INVALID_SYNTAX) + +#define NS_ERROR_LDAP_NO_SUCH_OBJECT \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NO_SUCH_OBJECT) + +#define NS_ERROR_LDAP_ALIAS_PROBLEM \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::ALIAS_PROBLEM) + +#define NS_ERROR_LDAP_INVALID_DN_SYNTAX \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INVALID_DN_SYNTAX) + +#define NS_ERROR_LDAP_IS_LEAF \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::IS_LEAF) + +#define NS_ERROR_LDAP_ALIAS_DEREF_PROBLEM \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::ALIAS_DEREF_PROBLEM) + +#define NS_ERROR_LDAP_INAPPROPRIATE_AUTH \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INAPPROPRIATE_AUTH) + +#define NS_ERROR_LDAP_INVALID_CREDENTIALS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INVALID_CREDENTIALS) + +#define NS_ERROR_LDAP_INSUFFICIENT_ACCESS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INSUFFICIENT_ACCESS) + +#define NS_ERROR_LDAP_BUSY \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::BUSY) + +#define NS_ERROR_LDAP_UNAVAILABLE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::UNAVAILABLE) + +#define NS_ERROR_LDAP_UNWILLING_TO_PERFORM \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::UNWILLING_TO_PERFORM) + +#define NS_ERROR_LDAP_LOOP_DETECT \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::LOOP_DETECT) + +#define NS_ERROR_LDAP_SORT_CONTROL_MISSING \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::SORT_CONTROL_MISSING) + +#define NS_ERROR_LDAP_INDEX_RANGE_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::INDEX_RANGE_ERROR) + +#define NS_ERROR_LDAP_NAMING_VIOLATION \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NAMING_VIOLATION) + +#define NS_ERROR_LDAP_OBJECT_CLASS_VIOLATION \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::OBJECT_CLASS_VIOLATION) + +#define NS_ERROR_LDAP_NOT_ALLOWED_ON_NONLEAF \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NOT_ALLOWED_ON_NONLEAF) + +#define NS_ERROR_LDAP_NOT_ALLOWED_ON_RDN \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NOT_ALLOWED_ON_RDN) + +#define NS_ERROR_LDAP_ALREADY_EXISTS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::ALREADY_EXISTS) + +#define NS_ERROR_LDAP_NO_OBJECT_CLASS_MODS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NO_OBJECT_CLASS_MODS) + +#define NS_ERROR_LDAP_RESULTS_TOO_LARGE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::RESULTS_TOO_LARGE) + +#define NS_ERROR_LDAP_AFFECTS_MULTIPLE_DSAS \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::AFFECTS_MULTIPLE_DSAS) + +#define NS_ERROR_LDAP_OTHER \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::OTHER) + +#define NS_ERROR_LDAP_SERVER_DOWN \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::SERVER_DOWN) + +#define NS_ERROR_LDAP_LOCAL_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::LOCAL_ERROR) + +#define NS_ERROR_LDAP_ENCODING_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::ENCODING_ERROR) + +#define NS_ERROR_LDAP_DECODING_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::DECODING_ERROR) + +#define NS_ERROR_LDAP_TIMEOUT \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::TIMEOUT) + +#define NS_ERROR_LDAP_AUTH_UNKNOWN \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::AUTH_UNKNOWN) + +#define NS_ERROR_LDAP_FILTER_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::FILTER_ERROR) + +#define NS_ERROR_LDAP_USER_CANCELLED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::USER_CANCELLED) + +#define NS_ERROR_LDAP_PARAM_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::PARAM_ERROR) + +#define NS_ERROR_LDAP_NO_MEMORY \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NO_MEMORY) + +#define NS_ERROR_LDAP_CONNECT_ERROR \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::CONNECT_ERROR) + +#define NS_ERROR_LDAP_NOT_SUPPORTED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NOT_SUPPORTED) + +#define NS_ERROR_LDAP_CONTROL_NOT_FOUND \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::CONTROL_NOT_FOUND) + +#define NS_ERROR_LDAP_NO_RESULTS_RETURNED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::NO_RESULTS_RETURNED) + +#define NS_ERROR_LDAP_MORE_RESULTS_TO_RETURN \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::MORE_RESULTS_TO_RETURN) + +#define NS_ERROR_LDAP_CLIENT_LOOP \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::CLIENT_LOOP) + +#define NS_ERROR_LDAP_REFERRAL_LIMIT_EXCEEDED \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, \ + nsILDAPErrors::REFERRAL_LIMIT_EXCEEDED) + +%} diff --git a/ldap/xpcom/public/nsILDAPMessage.idl b/ldap/xpcom/public/nsILDAPMessage.idl new file mode 100644 index 0000000000..7b3298e48c --- /dev/null +++ b/ldap/xpcom/public/nsILDAPMessage.idl @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPBERValue; +interface nsILDAPOperation; + +%{C++ +#define NS_LDAPMESSAGE_CONTRACTID "@mozilla.org/network/ldap-message;1" +%} + +[scriptable, uuid(973ff50f-2002-4f0c-b57d-2242156139a2)] +interface nsILDAPMessage : nsISupports +{ + /** + * The Distinguished Name of the entry associated with this message. + * + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER-decoding + * @exception NS_ERROR_UNEXPECTED bug or memory corruption + */ + readonly attribute AUTF8String dn; + + /** + * Get all the attributes in this message. + * + * @exception NS_ERROR_OUT_OF_MEMORY + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + * @exception NS_ERROR_UNEXPECTED bug or memory corruption + * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER decoding + * + * @return array of all attributes in the current message + */ + void getAttributes(out unsigned long count, + [retval, array, size_is(count)] out string aAttributes); + + /** + * Get an array of all the attribute values in this message. + * + * @param attr The attribute whose values are to be returned + * @param count Number of values in the outbound array. + * @param values Array of values + * + * @exception NS_ERROR_UNEXPECTED Bug or memory corruption + * @exception NS_ERROR_LDAP_DECODING_ERROR Attribute not found or other + * decoding error. + * @exception NS_ERROR_OUT_OF_MEMORY + */ + void getValues(in string attr, out unsigned long count, + [retval, array, size_is(count)] out wstring values); + + /** + * The operation this message originated from + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to getter + */ + readonly attribute nsILDAPOperation operation; + + /** + * The result code (aka lderrno) for this message. + * + * IDL definitions for these constants live in nsILDAPErrors.idl. + * + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + */ + readonly attribute long errorCode; + + /** + * The result type of this message. Possible types listed below, the + * values chosen are taken from the draft-ietf-ldapext-ldap-c-api-04.txt + * and are the same ones used in the ldap.h include file from the Mozilla + * LDAP C SDK. + * + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + * @exception NS_ERROR_UNEXPECTED internal error (possible memory + * corruption) + */ + readonly attribute long type; + + /** + * Result of a bind operation + */ + const long RES_BIND = 0x61; + + /** + * An entry found in an search operation. + */ + const long RES_SEARCH_ENTRY = 0x64; + + /** + * An LDAPv3 search reference (a referral to another server) + */ + const long RES_SEARCH_REFERENCE = 0x73; + + /** + * The result of a search operation (i.e. the search is done; no more + * entries to follow). + */ + const long RES_SEARCH_RESULT = 0x65; + + /** + * The result of a modify operation. + */ + const long RES_MODIFY = 0x67; + + /** + * The result of an add operation + */ + const long RES_ADD = 0x69; + + /** + * The result of a delete operation + */ + const long RES_DELETE = 0x6B; + + /** + * The result of an modify DN operation + */ + const long RES_MODDN = 0x6D; + + /** + * The result of a compare operation + */ + const long RES_COMPARE = 0x6F; + + /** + * The result of an LDAPv3 extended operation + */ + const long RES_EXTENDED = 0x78; + + /** + * get an LDIF-like string representation of this message + * + * @return unicode encoded string representation. + */ + wstring toUnicode(); + + /** + * Additional error information optionally sent by the server. + */ + readonly attribute AUTF8String errorMessage; + + /** + * In LDAPv3, when the server returns any of the following errors: + * NO_SUCH_OBJECT, ALIAS_PROBLEM, INVALID_DN_SYNTAX, ALIAS_DEREF_PROBLEM, + * it also returns the closest existing DN to the entry requested. + */ + readonly attribute AUTF8String matchedDn; + + /** + * Get an array of all the attribute values in this message (a wrapper + * around the LDAP C SDK's get_values_len()). + * + * @param attr The attribute whose values are to be returned + * @param count Number of values in the outbound array. + * @param values Array of nsILDAPBERValue objects + * + * @exception NS_ERROR_UNEXPECTED Bug or memory corruption + * @exception NS_ERROR_LDAP_DECODING_ERROR Attribute not found or other + * decoding error. + * @exception NS_ERROR_OUT_OF_MEMORY + */ + void getBinaryValues(in string attr, out unsigned long count, + [retval, array, size_is(count)] out nsILDAPBERValue values); +}; diff --git a/ldap/xpcom/public/nsILDAPMessageListener.idl b/ldap/xpcom/public/nsILDAPMessageListener.idl new file mode 100644 index 0000000000..8d4da5354f --- /dev/null +++ b/ldap/xpcom/public/nsILDAPMessageListener.idl @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPMessage; + +interface nsILDAPConnection; + +/** + * A callback interface to be implemented by any objects that want to + * receive results from an nsILDAPOperation (ie nsILDAPMessages) as they + * come in. + */ +[scriptable, uuid(dc721d4b-3ff2-4387-a80c-5e29545f774a)] +interface nsILDAPMessageListener : nsISupports +{ + /** + * Messages received are passed back via this function. + * + * @arg aMessage The message that was returned, NULL if none was. + * + * XXX semantics of NULL? + */ + void onLDAPMessage(in nsILDAPMessage aMessage); + + /** + * Notify the listener that the Init has completed, passing + * in the results from the connection initialization. The + * Reason for this is to allow us to do asynchronous DNS + * lookups, preresolving hostnames. + * + * @arg aConn The LDAP connection in question + * @arg aStatus The result from the LDAP connection init + */ + void onLDAPInit(in nsILDAPConnection aConn, in nsresult aStatus); +}; diff --git a/ldap/xpcom/public/nsILDAPModification.idl b/ldap/xpcom/public/nsILDAPModification.idl new file mode 100644 index 0000000000..6c93316e99 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPModification.idl @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsILDAPBERValue; +interface nsIArray; + +[scriptable, uuid(f64ef501-0623-11d6-a7f2-b65476fc49dc)] +interface nsILDAPModification : nsISupports +{ + /** + * The operation to perform. + */ + attribute long operation; + + /** + * Add operation + */ + const long MOD_ADD = 0x00; + + /** + * Delete operation + */ + const long MOD_DELETE = 0x01; + + /** + * Replace operation + */ + const long MOD_REPLACE = 0x02; + + /** + * Values are BER encoded + */ + const long MOD_BVALUES = 0x80; + + /** + * The attribute to modify. + */ + attribute ACString type; + + /** + * The array of values this modification sets for the attribute + */ + attribute nsIArray values; + + /** + * Function that allows all the attributes to be set at the same + * time to avoid multiple function calls. + */ + void setUpModification(in long aOperation, in ACString aType, + in nsIArray aValues); + + void setUpModificationOneValue(in long aOperation, in ACString aType, + in nsILDAPBERValue aValue); +}; diff --git a/ldap/xpcom/public/nsILDAPOperation.idl b/ldap/xpcom/public/nsILDAPOperation.idl new file mode 100644 index 0000000000..f3b9e6e7a2 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPOperation.idl @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsILDAPConnection.idl" +#include "nsIAuthModule.idl" + +interface nsILDAPMessage; +interface nsILDAPMessageListener; +interface nsILDAPModification; +interface nsIMutableArray; +interface nsIArray; + +%{C++ +#define NS_LDAPOPERATION_CONTRACTID "@mozilla.org/network/ldap-operation;1" +%} + +// XXXdmose check to make sure ctl-related err codes documented + +typedef uint32_t PRIntervalTime; + +[scriptable, uuid(4dfb1b19-fc8f-4525-92e7-f97b78a9747a)] +interface nsILDAPOperation : nsISupports +{ + /** + * The connection this operation is on. + * + * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in + */ + readonly attribute nsILDAPConnection connection; + + /** + * Callback for individual result messages related to this operation (set + * by the init() method). This is actually an nsISupports proxy object, + * as the callback will happen from another thread. + * + * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in + */ + readonly attribute nsILDAPMessageListener messageListener; + + /** + * The message-id associated with this operation. + * + * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in + */ + readonly attribute long messageID; + + /** + * private parameter (anything caller desires) + */ + attribute nsISupports closure; + + /** + * No time and/or size limit specified + */ + const long NO_LIMIT = 0; + + /** + * If specified, these arrays of nsILDAPControls are passed into the LDAP + * C SDK for any extended operations (ie method calls on this interface + * ending in "Ext"). + */ + attribute nsIMutableArray serverControls; + attribute nsIMutableArray clientControls; + + /** + * Initializes this operation. Must be called prior to initiating + * any actual operations. Note that by default, the aMessageListener + * callbacks happen on the LDAP connection thread. If you need them + * to happen on the main thread (or any other thread), then you should + * created an nsISupports proxy object and pass that in. + * + * @param aConnection connection this operation should use + * @param aMessageListener interface used to call back the results. + * @param aClosure private parameter (anything caller desires) + * + * @exception NS_ERROR_ILLEGAL_VALUE a NULL pointer was passed in + * @exception NS_ERROR_UNEXPECTED failed to get connection handle + */ + void init(in nsILDAPConnection aConnection, + in nsILDAPMessageListener aMessageListener, + in nsISupports aClosure); + + /** + * Asynchronously authenticate to the LDAP server. + * + * @param passwd the password used for binding; NULL for anon-binds + * + * @exception NS_ERROR_LDAP_ENCODING_ERROR problem encoding bind request + * @exception NS_ERROR_LDAP_SERVER_DOWN server down (XXX rebinds?) + * @exception NS_ERROR_LDAP_CONNECT_ERROR connection failed or lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_UNEXPECTED internal error + */ + void simpleBind(in AUTF8String passwd); + + /** + * Asynchronously perform a SASL bind against the LDAP server + * + * @param service the host name of the service being connected to + * @param mechanism the name of the SASL mechanism in use + * @param authModule the nsIAuthModule to be used to perform the operation + * + */ + void saslBind(in ACString service, in ACString mechanism, + in nsIAuthModule authModule); + + /** + * Continue a SASL bind operation + * + * @param token the next SASL token to send to the server + * @param tokenLen the length of the token to send + * + */ + void saslStep(in string token, in unsigned long tokenLen); + + /** + * Kicks off an asynchronous add request. The "ext" stands for + * "extensions", and is intended to convey that this method will + * eventually support the extensions described in the + * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft. + * + * @param aBaseDn Base DN to add + * @param aModCount Number of modifications + * @param aMods Array of modifications + * + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version + * of the LDAP protocol that the + * client is using + * @exception NS_ERROR_UNEXPECTED an unexpected error has + * occurred + * + * XXX doesn't currently handle LDAPControl params + */ + void addExt(in AUTF8String aBaseDn, in nsIArray aMods); + + /** + * Kicks off an asynchronous delete request. The "ext" stands for + * "extensions", and is intended to convey that this method will + * eventually support the extensions described in the + * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft. + * + * @param aBaseDn Base DN to delete + * + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version + * of the LDAP protocol that the + * client is using + * @exception NS_ERROR_UNEXPECTED an unexpected error has + * occurred + * + * XXX doesn't currently handle LDAPControl params + */ + void deleteExt(in AUTF8String aBaseDn); + + /** + * Kicks off an asynchronous modify request. The "ext" stands for + * "extensions", and is intended to convey that this method will + * eventually support the extensions described in the + * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft. + * + * @param aBaseDn Base DN to modify + * @param aModCount Number of modifications + * @param aMods Array of modifications + * + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version + * of the LDAP protocol that the + * client is using + * @exception NS_ERROR_UNEXPECTED an unexpected error has + * occurred + * + * XXX doesn't currently handle LDAPControl params + */ + void modifyExt(in AUTF8String aBaseDn, in nsIArray aMods); + + /** + * Kicks off an asynchronous rename request. + * + * @param aBaseDn Base DN to rename + * @param aNewRDn New relative DN + * @param aNewParent DN of the new parent under which to move the + * entry + * @param aDeleteOldRDn Indicates whether to remove the old relative + * DN as a value in the entry or not + * + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version + * of the LDAP protocol that the + * client is using + * @exception NS_ERROR_UNEXPECTED an unexpected error has + * occurred + * + * XXX doesn't currently handle LDAPControl params + */ + void rename(in AUTF8String aBaseDn, in AUTF8String aNewRDn, + in AUTF8String aNewParent, in boolean aDeleteOldRDn); + + /** + * Kicks off an asynchronous search request. The "ext" stands for + * "extensions", and is intended to convey that this method will + * eventually support the extensions described in the + * draft-ietf-ldapext-ldap-c-api-04.txt Internet Draft. + * + * @param aBaseDn Base DN to search + * @param aScope One of SCOPE_{BASE,ONELEVEL,SUBTREE} + * @param aFilter Search filter + * @param aAttributes Comma separated list of values, holding the + * attributes we need + * @param aTimeOut How long to wait + * @param aSizeLimit Maximum number of entries to return. + * + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_LDAP_NOT_SUPPORTED not supported in the version + * of the LDAP protocol that the + * client is using + * @exception NS_ERROR_LDAP_FILTER_ERROR + * @exception NS_ERROR_UNEXPECTED + */ + void searchExt(in AUTF8String aBaseDn, in int32_t aScope, + in AUTF8String aFilter, in ACString aAttributes, + in PRIntervalTime aTimeOut, in int32_t aSizeLimit); + + /** + * Cancels an async operation that is in progress. + * + * XXX controls not supported yet + * + * @exception NS_ERROR_NOT_IMPLEMENTED server or client controls + * were set on this object + * @exception NS_ERROR_NOT_INITIALIZED operation not initialized + * @exception NS_ERROR_LDAP_ENCODING_ERROR error during BER-encoding + * @exception NS_ERROR_LDAP_SERVER_DOWN the LDAP server did not + * receive the request or the + * connection was lost + * @exception NS_ERROR_OUT_OF_MEMORY out of memory + * @exception NS_ERROR_INVALID_ARG invalid argument + * @exception NS_ERROR_UNEXPECTED internal error + */ + void abandonExt(); +}; diff --git a/ldap/xpcom/public/nsILDAPServer.idl b/ldap/xpcom/public/nsILDAPServer.idl new file mode 100644 index 0000000000..5fc9522e41 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPServer.idl @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsILDAPConnection.idl" + +interface nsILDAPURL; + +/** + * this interface provides a way to store, retrieve and manipulate + * information related to a specific LDAP server. This includes the + * LDAP URL, as well as certain user specific data (e.g. credentials). + * + * The implementation of nsILDAPService relies heavily on this + * interface, managing all LDAP connections (nsILDAPConnection). + * The Service manages LDAP connections (connect and disconnect etc.), + * using the information available from these LDAP Server objects. + */ + + +[scriptable, uuid(8aa717a4-1dd2-11b2-99c7-f01e2d449ded)] +interface nsILDAPServer : nsISupports { + + /** + * unique identifier for this server, used (typically) to identify a + * particular server object in a list of servers. This key can be + * any "string", but in our case it will most likely be the same + * identifier as used in a Mozilla preferences files. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + attribute wstring key; + + /** + * the password string used to bind to this server. An empty + * string here implies binding as anonymous. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + attribute AUTF8String password; + + /** + * the user name to authenticate as. An empty string here would + * imply binding as anonymous. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + attribute AUTF8String username; + + /** + * the bind DN (Distinguished Name). + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + attribute AUTF8String binddn; + + /** maximum number of hits we want to accept from an LDAP search + * operation. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + */ + attribute unsigned long sizelimit; + + /** + * the URL for this server. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + */ + attribute nsILDAPURL url; + + /** + * protocol version to be used (see nsILDAPConnection.idl for constants) + * Defaults to 3. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer passed to getter + * @exception NS_ERROR_INVALID_ARG Invalid version passed to setter + */ + attribute unsigned long protocolVersion; +}; diff --git a/ldap/xpcom/public/nsILDAPService.idl b/ldap/xpcom/public/nsILDAPService.idl new file mode 100644 index 0000000000..e4f8e75e6e --- /dev/null +++ b/ldap/xpcom/public/nsILDAPService.idl @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +interface nsILDAPServer; +interface nsILDAPConnection; +interface nsILDAPMessageListener; + +/** + * This interface provides an LDAP connection management service. + * It's used to cache already established LDAP connections, as well + * as reaping unused connections after a certain time period. This + * is done completely asynchronously, using callback functions. + */ + + +[scriptable, uuid(69de6fbc-2e8c-4482-bf14-358d68b785d1)] +interface nsILDAPService : nsISupports { + + /** + * Add a (possibly) new LDAP server entry to the service. A + * server entry holds information about the host, port and + * other components of the LDAP URL, as well as information + * used for binding a connection to the LDAP server. + * + * An LDAP Server entry (nsILDAPServer) contains the URL, + * user credentials, and other information related to the actual + * server itself. It is used for connecting, binding, rebinding, + * setting timeouts and so forth. + * + * @param aServer an nsILDAPServer object + * + * @exception NS_ERROR_FAILURE the server has already been + * added to the service + * @exception NS_ERROR_NULL_POINTER NULL pointer + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + void addServer(in nsILDAPServer aServer); + + /** + * Mark an LDAP server, in the Service, as a candidate for + * deletion. If there are still leases ("users") of this server, + * the operation fails. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE either the server doesn't + * exist, or there are still + * leases oustanding + */ + void deleteServer(in wstring aKey); + + /** + * Get the nsILDAPServer object for the specified server entry + * in the service. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_NULL_POINTER NULL pointer + */ + nsILDAPServer getServer(in wstring aKey); + + /** + * Request a connection from the service, asynchronously. If there is + * one "cached" already, we will actually call the callback function + * before returning from this function. This might be considered a bug, + * but for now be aware of this (see Bugzilla bug #75989). + * + * Calling this method does not increment the leases on this connection, + * you'll have to use the getConnection() method to actually get the + * connection itself (presumably from the callback/listener object). + * The listener needs to implement nsILDAPMessageListener, providing + * the OnLDAPMessage() method. + * + * @param aKey unique key identifying the server entry + * @param aMessageListener the listener object, which we will call + * when the LDAP bind message is available + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key, + * or we were unable to get a + * connection properly to the server + * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_UNEXPECTED unknown or unexpected error... + */ + void requestConnection(in wstring aKey, + in nsILDAPMessageListener aListener); + + /** + * This is the nsLDAPConnection object related to this server. + * This does increase the lease counter on the object, so you have + * to call the releaseConnection() method to return it. It is + * important that you do this in matching pairs, and that you do + * not keep any dangling references to an object around after you + * have called the releaseConnection() method. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_NULL_POINTER NULL pointer + */ + nsILDAPConnection getConnection(in wstring aKey); + + /** + * Release the lease on a (cached) LDAP connection, making it a + * potential candidate for disconnection. Note that this will not + * delete the actual LDAP server entry in the service, it's still + * registered and can be used in future calls to requestConnection(). + * + * This API might be deprecated in the future, once we figure out how + * to use weak references to support our special needs for reference + * counting. For the time being, it is vital that you call this function + * when you're done with a Connection, and that you do not keep any + * copies of the Connection object lingering around. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + void releaseConnection(in wstring aKey); + + /** + * If we detect that a connection is broken (server disconnected us, + * or any other problem with the link), we need to try to reestablish + * the connection. This is very similar to requestConnection(), + * except you use this when detecting an error with a connection + * that is being cached. + * + * @param aKey unique key identifying the server entry + * @param aMessageListener the listener object, which we will call + * when the LDAP bind message is available + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key, + * or we were unable to get a + * connection properly to the server + * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_UNEXPECTED unknown or unexpected error... + */ + void reconnectConnection(in wstring aKey, + in nsILDAPMessageListener aListener); + + /** + * Generates and returns an LDAP search filter by substituting + * aValue, aAttr, aPrefix, and aSuffix into aPattern. + * + * The only good documentation I'm aware of for this function is + * at + * and + * + * Unfortunately, this does not currently seem to be available + * under any open source license, so I can't include that + * documentation here in the doxygen comments. + * + * @param aMaxSize maximum size (in char) of string to be + * created and returned (including final \0) + * @param aPattern pattern to be used for the filter + * @param aPrefix prefix to prepend to the filter + * @param aSuffix suffix to be appended to the filer + * @param aAttr replacement for %a in the pattern + * @param aValue replacement for %v in the pattern + * + * @exception NS_ERROR_INVALID_ARG invalid parameter passed in + * @exception NS_ERROR_OUT_OF_MEMORY allocation failed + * @exception NS_ERROR_NOT_AVAILABLE filter longer than maxsiz chars + * @exception NS_ERROR_UNEXPECTED ldap_create_filter returned + * unexpected error code + */ + AUTF8String createFilter(in unsigned long aMaxSize, in AUTF8String aPattern, + in AUTF8String aPrefix, in AUTF8String aSuffix, + in AUTF8String aAttr, in AUTF8String aValue); + + /** + * Parses a distinguished name (DN) and returns the relative DN, + * base DN and the list of attributes that make up the relative DN. + * + * @param dn DN to parse + * @param rdn The relative DN for the given DN + * @param baseDn The base DN for the given DN + * @param rdnCount Number of values in the outbound attributes array. + * @param rdnAttrs Array of attribute names + * + */ + void parseDn(in string dn, out AUTF8String rdn, out AUTF8String baseDn, + out unsigned long rdnCount, + [retval, array, size_is(rdnCount)] out string rdnAttrs); +}; diff --git a/ldap/xpcom/public/nsILDAPSyncQuery.idl b/ldap/xpcom/public/nsILDAPSyncQuery.idl new file mode 100644 index 0000000000..2ae3307f05 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPSyncQuery.idl @@ -0,0 +1,27 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +interface nsILDAPURL; + + +[scriptable, uuid (0308fb36-1dd2-11b2-b16f-8510e8c5311a)] +interface nsILDAPSyncQuery : nsISupports { + + /** + * getQueryResults + * + * Create a new LDAP connection do a synchronous LDAP search and return + * the results. + * @param aServerURL - LDAP URL with parameters to a LDAP search + * ("ldap://host/base?attributes?one/sub?filter") + * @param aProtocolVersion - LDAP protocol version to use for connection + * (nsILDAPConnection.idl has symbolic constants) + * @return results + */ + wstring getQueryResults (in nsILDAPURL aServerURL, + in unsigned long aProtocolVersion); + +}; diff --git a/ldap/xpcom/public/nsILDAPURL.idl b/ldap/xpcom/public/nsILDAPURL.idl new file mode 100644 index 0000000000..dae76496d3 --- /dev/null +++ b/ldap/xpcom/public/nsILDAPURL.idl @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIURI.idl" + +%{C++ +#define NS_LDAPURL_CONTRACTID "@mozilla.org/network/ldap-url;1" +%} + +/** + * Strings in methods inherited from nsIURI, which are using XPIDL + * |string| types, are expected to be UTF8 encoded. All such strings + * in this interface, except attribute types (e.g. "cn"), should in fact + * be UTF8. It's important to remember that attributes can not be UTF8, + * they can only be of a limited subset of ASCII (see RFC 2251). + */ + +[scriptable, uuid(8e3a6d33-2e68-40ba-8f94-6ac03f69066e)] +interface nsILDAPURL : nsIURI { + /** + * Initialize an LDAP URL + * + * @param aUrlType - one of the URLTYPE_ flags @seealso nsIStandardURL + * @param aDefaultPort - if the port parsed from the URL string matches + * this port, then the port will be removed from the + * canonical form of the URL. + * @param aSpec - URL string. + * @param aOriginCharset - the charset from which this URI string + * originated. this corresponds to the charset + * that should be used when communicating this + * URI to an origin server, for example. if + * null, then provide aBaseURI implements this + * interface, the origin charset of aBaseURI will + * be assumed, otherwise defaulting to UTF-8 (i.e., + * no charset transformation from aSpec). + * @param aBaseURI - if null, aSpec must specify an absolute URI. + * otherwise, aSpec will be resolved relative + * to aBaseURI. + */ + void init(in unsigned long aUrlType, + in long aDefaultPort, + in AUTF8String aSpec, + in string aOriginCharset, + in nsIURI aBaseURI); + + /** + * The distinguished name of the URL (ie the base DN for the search). + * This string is expected to be a valid UTF8 string. + * + * for the getter: + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory + */ + attribute AUTF8String dn; + + /** + * The attributes to get for this URL, in comma-separated format. If the + * list is empty, all attributes are requested. + */ + attribute ACString attributes; + + /** + * Add one attribute to the array of attributes to request. If the + * attribute is already in our array, this becomes a noop. + * + * @param aAttribute An LDAP attribute (e.g. "cn") + */ + void addAttribute(in ACString aAttribute); + + /** + * Remove one attribute from the array of attributes to request. If + * the attribute didn't exist in the array, this becomes a noop. + * + * @param aAttribute An LDAP attribute (e.g. "cn") + * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory + */ + void removeAttribute(in ACString aAttribute); + + /** + * Test if an attribute is in our list of attributes already + * + * @param aAttribute An LDAP attribute (e.g. "cn") + * @return boolean Truth value + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + */ + boolean hasAttribute(in ACString aAttribute); + + /** + * The scope of the search. defaults to SCOPE_BASE. + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_MALFORMED_URI Illegal base to SET method + */ + attribute long scope; + + /** + * Search just the base object + */ + const long SCOPE_BASE = 0; + + /** + * Search only the children of the base object + */ + const long SCOPE_ONELEVEL = 1; + + /** + * Search the entire subtree under and including the base object + */ + const long SCOPE_SUBTREE = 2; + + /** + * The search filter. "(objectClass=*)" is the default. + */ + attribute AUTF8String filter; + + /** + * Any options defined for this URL (check options using a bitwise and) + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to GET method + * @exception NS_ERROR_OUT_OF_MEMORY Ran out of memory + */ + attribute unsigned long options; + + /** + * If this is set/true, this is an ldaps: URL, not an ldap: URL + */ + const unsigned long OPT_SECURE = 0x01; +}; diff --git a/ldap/xpcom/src/ldapComponents.manifest b/ldap/xpcom/src/ldapComponents.manifest new file mode 100644 index 0000000000..482bbd55e6 --- /dev/null +++ b/ldap/xpcom/src/ldapComponents.manifest @@ -0,0 +1,5 @@ +component {b3de9249-b0e5-4c12-8d91-c9a434fd80f5} nsLDAPProtocolHandler.js +contract @mozilla.org/network/protocol;1?name=ldap {b3de9249-b0e5-4c12-8d91-c9a434fd80f5} + +component {c85a5ef2-9c56-445f-b029-76889f2dd29b} nsLDAPProtocolHandler.js +contract @mozilla.org/network/protocol;1?name=ldaps {c85a5ef2-9c56-445f-b029-76889f2dd29b} diff --git a/ldap/xpcom/src/moz.build b/ldap/xpcom/src/moz.build new file mode 100644 index 0000000000..06753e3173 --- /dev/null +++ b/ldap/xpcom/src/moz.build @@ -0,0 +1,53 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'nsLDAPBERElement.cpp', + 'nsLDAPBERValue.cpp', + 'nsLDAPConnection.cpp', + 'nsLDAPControl.cpp', + 'nsLDAPMessage.cpp', + 'nsLDAPModification.cpp', + 'nsLDAPOperation.cpp', + 'nsLDAPProtocolModule.cpp', + 'nsLDAPSecurityGlue.cpp', + 'nsLDAPServer.cpp', + 'nsLDAPService.cpp', + 'nsLDAPURL.cpp', +] + +if CONFIG['MOZ_PREF_EXTENSIONS']: + SOURCES += ['nsLDAPSyncQuery.cpp'] + DEFINES['MOZ_PREF_EXTENSIONS'] = True + +EXTRA_COMPONENTS += [ + 'ldapComponents.manifest', + 'nsLDAPProtocolHandler.js', +] + +USE_LIBS += [ + 'ldapsdks', +] + +if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']: + XPCOMBinaryComponent('mozldap') + USE_LIBS += [ + 'nspr', + 'xpcomglue_s', + 'xul', + ] +# js needs to come after xul for now, because it is an archive and its content +# is discarded when it comes first. + USE_LIBS += [ + 'js', + ] +else: + Library('mozldap') + FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/ldap/c-sdk/include', +] + diff --git a/ldap/xpcom/src/nsLDAPBERElement.cpp b/ldap/xpcom/src/nsLDAPBERElement.cpp new file mode 100644 index 0000000000..1ea96793e8 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPBERElement.cpp @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPBERElement.h" +#include "nsStringGlue.h" +#include "nsCOMPtr.h" +#include "nsLDAPBERValue.h" + +NS_IMPL_ISUPPORTS(nsLDAPBERElement, nsILDAPBERElement) + +nsLDAPBERElement::nsLDAPBERElement() + : mElement(0) +{ +} + +nsLDAPBERElement::~nsLDAPBERElement() +{ + if (mElement) { + // anything inside here is not something that we own separately from + // this object, so free it + ber_free(mElement, 1); + } + + return; +} + +NS_IMETHODIMP +nsLDAPBERElement::Init(nsILDAPBERValue *aValue) +{ + if (aValue) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + mElement = ber_alloc_t(LBER_USE_DER); + return mElement ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +/* void putString (in AUTF8String aString, in unsigned long aTag); */ +NS_IMETHODIMP +nsLDAPBERElement::PutString(const nsACString & aString, uint32_t aTag, + uint32_t *aBytesWritten) +{ + // XXX if the string translation feature of the C SDK is ever used, + // this const_cast will break + int i = ber_put_ostring(mElement, + const_cast(PromiseFlatCString(aString).get()), + aString.Length(), aTag); + + if (i < 0) { + return NS_ERROR_FAILURE; + } + + *aBytesWritten = i; + return NS_OK; +} + +/* void startSet (); */ +NS_IMETHODIMP nsLDAPBERElement::StartSet(uint32_t aTag) +{ + int i = ber_start_set(mElement, aTag); + + if (i < 0) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +/* void putSet (); */ +NS_IMETHODIMP nsLDAPBERElement::PutSet(uint32_t *aBytesWritten) +{ + int i = ber_put_set(mElement); + + if (i < 0) { + return NS_ERROR_FAILURE; + } + + *aBytesWritten = i; + return NS_OK; +} + +/* nsILDAPBERValue flatten (); */ +NS_IMETHODIMP nsLDAPBERElement::GetAsValue(nsILDAPBERValue **_retval) +{ + // create the value object + nsCOMPtr berValue = new nsLDAPBERValue(); + + if (!berValue) { + NS_ERROR("nsLDAPBERElement::GetAsValue(): out of memory" + " creating nsLDAPBERValue object"); + return NS_ERROR_OUT_OF_MEMORY; + } + + struct berval *bv; + if ( ber_flatten(mElement, &bv) < 0 ) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = berValue->Set(bv->bv_len, + reinterpret_cast(bv->bv_val)); + + // whether or not we've succeeded, we're done with the ldap c sdk struct + ber_bvfree(bv); + + // as of this writing, this error can only be NS_ERROR_OUT_OF_MEMORY + if (NS_FAILED(rv)) { + return rv; + } + + // return the raw interface pointer + NS_ADDREF(*_retval = berValue.get()); + + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPBERElement.h b/ldap/xpcom/src/nsLDAPBERElement.h new file mode 100644 index 0000000000..bdeb9c4391 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPBERElement.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "lber.h" +#include "nsILDAPBERElement.h" + +// 070af769-b7f5-40e7-81be-196155ead84c +#define NS_LDAPBERELEMENT_CID \ + { 0x070af769, 0xb7f5, 0x40e7, \ + { 0x81, 0xbe, 0x19, 0x61, 0x55, 0xea, 0xd8, 0x4c }} + +class nsLDAPBERElement final : public nsILDAPBERElement +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSILDAPBERELEMENT + + nsLDAPBERElement(); + +private: + ~nsLDAPBERElement(); + + BerElement *mElement; + +protected: +}; + diff --git a/ldap/xpcom/src/nsLDAPBERValue.cpp b/ldap/xpcom/src/nsLDAPBERValue.cpp new file mode 100644 index 0000000000..d71073b7ac --- /dev/null +++ b/ldap/xpcom/src/nsLDAPBERValue.cpp @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPBERValue.h" +#include "nsMemory.h" +#include "nsStringGlue.h" + +NS_IMPL_ISUPPORTS(nsLDAPBERValue, nsILDAPBERValue) + +nsLDAPBERValue::nsLDAPBERValue() : mValue(0), mSize(0) +{ +} + +nsLDAPBERValue::~nsLDAPBERValue() +{ + if (mValue) { + free(mValue); + } +} + +// void get (out unsigned long aCount, +// [array, size_is (aCount), retval] out octet aRetVal); */ +NS_IMETHODIMP +nsLDAPBERValue::Get(uint32_t *aCount, uint8_t **aRetVal) +{ + // if mSize = 0, return a count of a 0 and a null pointer + + if (mSize) { + // get a buffer to hold a copy of the data + // + uint8_t *array = static_cast(moz_xmalloc(mSize)); + + if (!array) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // copy and return + // + memcpy(array, mValue, mSize); + *aRetVal = array; + } else { + *aRetVal = 0; + } + + *aCount = mSize; + return NS_OK; +} + +// void set(in unsigned long aCount, +// [array, size_is(aCount)] in octet aValue); +NS_IMETHODIMP +nsLDAPBERValue::Set(uint32_t aCount, uint8_t *aValue) +{ + // get rid of any old value being held here + // + if (mValue) { + free(mValue); + } + + // if this is a non-zero value, allocate a buffer and copy + // + if (aCount) { + // get a buffer to hold a copy of this data + // + mValue = static_cast(moz_xmalloc(aCount)); + if (!mValue) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // copy the data and return + // + memcpy(mValue, aValue, aCount); + } else { + // otherwise just set it to null + // + mValue = 0; + } + + mSize = aCount; + return NS_OK; +} + +// void setFromUTF8(in AUTF8String aValue); +// +NS_IMETHODIMP +nsLDAPBERValue::SetFromUTF8(const nsACString & aValue) +{ + // get rid of any old value being held here + // + if (mValue) { + free(mValue); + } + + // copy the data and return + // + mSize = aValue.Length(); + if (mSize) { + mValue = reinterpret_cast(ToNewCString(aValue)); + } else { + mValue = 0; + } + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPBERValue.h b/ldap/xpcom/src/nsLDAPBERValue.h new file mode 100644 index 0000000000..d7f563f03e --- /dev/null +++ b/ldap/xpcom/src/nsLDAPBERValue.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsLDAPBERValue_h_ +#define _nsLDAPBERValue_h_ + +#include "ldap.h" +#include "nsILDAPBERValue.h" + +// 7c9fa10e-1dd2-11b2-a097-ac379e6803b2 +// +#define NS_LDAPBERVALUE_CID \ +{ 0x7c9fa10e, 0x1dd2, 0x11b2, \ + {0xa0, 0x97, 0xac, 0x37, 0x9e, 0x68, 0x03, 0xb2 }} + +class nsLDAPBERValue : public nsILDAPBERValue +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPBERVALUE + + nsLDAPBERValue(); + +protected: + virtual ~nsLDAPBERValue(); + + /** + * nsLDAPControl needs to be able to grovel through this without an + * an extra copy + */ + friend class nsLDAPControl; + + uint8_t *mValue; // pointer to an array + uint32_t mSize; // size of the value, in bytes +}; + +#endif // _nsLDAPBERValue_h_ diff --git a/ldap/xpcom/src/nsLDAPConnection.cpp b/ldap/xpcom/src/nsLDAPConnection.cpp new file mode 100644 index 0000000000..834e803b4c --- /dev/null +++ b/ldap/xpcom/src/nsLDAPConnection.cpp @@ -0,0 +1,737 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPInternal.h" +#include "nsIServiceManager.h" +#include "nsStringGlue.h" +#include "nsIComponentManager.h" +#include "nsIDNSRecord.h" +#include "nsLDAPConnection.h" +#include "nsLDAPMessage.h" +#include "nsThreadUtils.h" +#include "nsIConsoleService.h" +#include "nsIDNSService.h" +#include "nsINetAddr.h" +#include "nsIRequestObserver.h" +#include "nsError.h" +#include "nsLDAPOperation.h" +#include "nsILDAPErrors.h" +#include "nsIClassInfoImpl.h" +#include "nsILDAPURL.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "nsMemory.h" +#include "nsLDAPUtils.h" +#include "nsProxyRelease.h" +#include "mozilla/Logging.h" +#include "mozilla/Attributes.h" + +using namespace mozilla; + +const char kDNSServiceContractId[] = "@mozilla.org/network/dns-service;1"; + +// constructor +// +nsLDAPConnection::nsLDAPConnection() + : mConnectionHandle(nullptr), + mPendingOperationsMutex("nsLDAPConnection.mPendingOperationsMutex"), + mPendingOperations(10), + mSSL(false), + mVersion(nsILDAPConnection::VERSION3), + mDNSRequest(nullptr) +{ +} + +// destructor +// +nsLDAPConnection::~nsLDAPConnection() +{ + nsCOMPtr obsServ = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); + if (obsServ) + obsServ->RemoveObserver(this, "profile-change-net-teardown"); + Close(); +} + +NS_IMPL_ADDREF(nsLDAPConnection) +NS_IMPL_RELEASE(nsLDAPConnection) +NS_IMPL_CLASSINFO(nsLDAPConnection, NULL, nsIClassInfo::THREADSAFE, + NS_LDAPCONNECTION_CID) + +NS_INTERFACE_MAP_BEGIN(nsLDAPConnection) + NS_INTERFACE_MAP_ENTRY(nsILDAPConnection) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIDNSListener) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPConnection) + NS_IMPL_QUERY_CLASSINFO(nsLDAPConnection) +NS_INTERFACE_MAP_END_THREADSAFE +NS_IMPL_CI_INTERFACE_GETTER(nsLDAPConnection, nsILDAPConnection, + nsISupportsWeakReference, nsIDNSListener, + nsIObserver) + +NS_IMETHODIMP +nsLDAPConnection::Init(nsILDAPURL *aUrl, const nsACString &aBindName, + nsILDAPMessageListener *aMessageListener, + nsISupports *aClosure, uint32_t aVersion) +{ + NS_ENSURE_ARG_POINTER(aUrl); + NS_ENSURE_ARG_POINTER(aMessageListener); + + nsresult rv; + nsCOMPtr obsServ = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // We have to abort all LDAP pending operation before shutdown. + obsServ->AddObserver(this, "profile-change-net-teardown", true); + + // Save various items that we'll use later + mBindName.Assign(aBindName); + mClosure = aClosure; + mInitListener = aMessageListener; + + // Make sure we haven't called Init earlier, i.e. there's a DNS + // request pending. + NS_ASSERTION(!mDNSRequest, "nsLDAPConnection::Init() " + "Connection was already initialized\n"); + + // Check and save the version number + if (aVersion != nsILDAPConnection::VERSION2 && + aVersion != nsILDAPConnection::VERSION3) { + NS_ERROR("nsLDAPConnection::Init(): illegal version"); + return NS_ERROR_ILLEGAL_VALUE; + } + mVersion = aVersion; + + // Get the port number, SSL flag for use later, once the DNS server(s) + // has resolved the host part. + rv = aUrl->GetPort(&mPort); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t options; + rv = aUrl->GetOptions(&options); + NS_ENSURE_SUCCESS(rv, rv); + + mSSL = options & nsILDAPURL::OPT_SECURE; + + nsCOMPtr curThread = do_GetCurrentThread(); + if (!curThread) { + NS_ERROR("nsLDAPConnection::Init(): couldn't get current thread"); + return NS_ERROR_FAILURE; + } + + // Do the pre-resolve of the hostname, using the DNS service. This + // will also initialize the LDAP connection properly, once we have + // the IPs resolved for the hostname. This includes creating the + // new thread for this connection. + // + // XXX - What return codes can we expect from the DNS service? + // + nsCOMPtr + pDNSService(do_GetService(kDNSServiceContractId, &rv)); + + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPConnection::Init(): couldn't create the DNS Service object"); + return NS_ERROR_FAILURE; + } + + rv = aUrl->GetAsciiHost(mDNSHost); + NS_ENSURE_SUCCESS(rv, rv); + + // if the caller has passed in a space-delimited set of hosts, as the + // ldap c-sdk allows, strip off the trailing hosts for now. + // Soon, we'd like to make multiple hosts work, but now make + // at least the first one work. + LdapCompressWhitespace(mDNSHost); + + int32_t spacePos = mDNSHost.FindChar(' '); + // trim off trailing host(s) + if (spacePos != kNotFound) + mDNSHost.SetLength(spacePos); + + rv = pDNSService->AsyncResolve(mDNSHost, 0, this, curThread, + getter_AddRefs(mDNSRequest)); + + if (NS_FAILED(rv)) { + switch (rv) { + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_UNKNOWN_HOST: + case NS_ERROR_FAILURE: + case NS_ERROR_OFFLINE: + break; + + default: + rv = NS_ERROR_UNEXPECTED; + } + mDNSHost.Truncate(); + } + return rv; +} + +// this might get exposed to clients, so we've broken it +// out of the destructor. +void +nsLDAPConnection::Close() +{ + int rc; + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbinding\n")); + + if (mConnectionHandle) { + // note that the ldap_unbind() call in the 5.0 version of the LDAP C SDK + // appears to be exactly identical to ldap_unbind_s(), so it may in fact + // still be synchronous + // + rc = ldap_unbind(mConnectionHandle); +#ifdef PR_LOGGING + if (rc != LDAP_SUCCESS) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPConnection::Close(): %s\n", + ldap_err2string(rc))); + } +#endif + mConnectionHandle = nullptr; + } + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbound\n")); + + NS_ASSERTION(!mThread || NS_SUCCEEDED(mThread->Shutdown()), + "Failed to shutdown thread cleanly"); + + // Cancel the DNS lookup if needed, and also drop the reference to the + // Init listener (if still there). + // + if (mDNSRequest) { + mDNSRequest->Cancel(NS_ERROR_ABORT); + mDNSRequest = nullptr; + } + mInitListener = nullptr; + +} + +NS_IMETHODIMP +nsLDAPConnection::Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, "profile-change-net-teardown")) { + // Abort all ldap requests. + /* We cannot use enumerate function to abort operations because + * nsILDAPOperation::AbandonExt() is modifying list of operations + * and this leads to starvation. + * We have to do a copy of pending operations. + */ + nsTArray pending_operations; + { + MutexAutoLock lock(mPendingOperationsMutex); + for (auto iter = mPendingOperations.Iter(); !iter.Done(); iter.Next()) { + pending_operations.AppendElement(iter.UserData()); + } + } + for (uint32_t i = 0; i < pending_operations.Length(); i++) { + pending_operations[i]->AbandonExt(); + } + Close(); + } else { + NS_NOTREACHED("unexpected topic"); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::GetClosure(nsISupports **_retval) +{ + if (!_retval) { + return NS_ERROR_ILLEGAL_VALUE; + } + NS_IF_ADDREF(*_retval = mClosure); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::SetClosure(nsISupports *aClosure) +{ + mClosure = aClosure; + return NS_OK; +} + +// who we're binding as +// +// readonly attribute AUTF8String bindName +// +NS_IMETHODIMP +nsLDAPConnection::GetBindName(nsACString& _retval) +{ + _retval.Assign(mBindName); + return NS_OK; +} + +// wrapper for ldap_get_lderrno +// XXX should copy before returning +// +NS_IMETHODIMP +nsLDAPConnection::GetLdErrno(nsACString& matched, nsACString& errString, + int32_t *_retval) +{ + char *match, *err; + + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = ldap_get_lderrno(mConnectionHandle, &match, &err); + matched.Assign(match); + errString.Assign(err); + return NS_OK; +} + +// return the error string corresponding to GetLdErrno. +// +// XXX - deal with optional params +// XXX - how does ldap_perror know to look at the global errno? +// +NS_IMETHODIMP +nsLDAPConnection::GetErrorString(char16_t **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + // get the error string + // + char *rv = ldap_err2string(ldap_get_lderrno(mConnectionHandle, 0, 0)); + if (!rv) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // make a copy using the XPCOM shared allocator + // + *_retval = ToNewUnicode(NS_ConvertUTF8toUTF16(rv)); + if (!*_retval) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +/** + * Add an nsILDAPOperation to the list of operations pending on + * this connection. This is also mainly intended for use by the + * nsLDAPOperation code. + */ +nsresult +nsLDAPConnection::AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation) +{ + NS_ENSURE_ARG_POINTER(aOperation); + + nsIRunnable* runnable = new nsLDAPConnectionRunnable(aOperationID, aOperation, + this); + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Put((uint32_t)aOperationID, aOperation); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("pending operation added; total pending operations now = %d\n", + mPendingOperations.Count())); + } + + nsresult rv; + if (!mThread) + { + rv = NS_NewThread(getter_AddRefs(mThread), runnable); + NS_ENSURE_SUCCESS(rv, rv); + } + else + { + rv = mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +/** + * Remove an nsILDAPOperation from the list of operations pending on this + * connection. Mainly intended for use by the nsLDAPOperation code. + * + * @param aOperation operation to add + * @exception NS_ERROR_INVALID_POINTER aOperation was NULL + * @exception NS_ERROR_OUT_OF_MEMORY out of memory + * @exception NS_ERROR_FAILURE could not delete the operation + * + * void removePendingOperation(in nsILDAPOperation aOperation); + */ +nsresult +nsLDAPConnection::RemovePendingOperation(uint32_t aOperationID) +{ + NS_ENSURE_TRUE(aOperationID > 0, NS_ERROR_UNEXPECTED); + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPConnection::RemovePendingOperation(): operation removed\n")); + + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Remove(aOperationID); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPConnection::RemovePendingOperation(): operation " + "removed; total pending operations now = %d\n", + mPendingOperations.Count())); + } + + return NS_OK; +} + +class nsOnLDAPMessageRunnable : public Runnable +{ +public: + nsOnLDAPMessageRunnable(nsLDAPMessage *aMsg, bool aClear) + : m_msg(aMsg) + , m_clear(aClear) + {} + NS_DECL_NSIRUNNABLE +private: + RefPtr m_msg; + bool m_clear; +}; + +NS_IMETHODIMP nsOnLDAPMessageRunnable::Run() +{ + // get the message listener object. + nsLDAPOperation *nsoperation = static_cast(m_msg->mOperation.get()); + nsCOMPtr listener; + nsresult rv = nsoperation->GetMessageListener(getter_AddRefs(listener)); + + if (m_clear) + { + // try to break cycles + nsoperation->Clear(); + } + + if (!listener) + { + NS_ERROR("nsLDAPConnection::InvokeMessageCallback(): probable " + "memory corruption: GetMessageListener() returned nullptr"); + return rv; + } + + return listener->OnLDAPMessage(m_msg); +} + +nsresult +nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle, + nsILDAPMessage *aMsg, + int32_t aOperation, + bool aRemoveOpFromConnQ) +{ +#if defined(DEBUG) + // We only want this being logged for debug builds so as not to affect performance too much. + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("InvokeMessageCallback entered\n")); +#endif + + // Get the operation. + nsCOMPtr operation; + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Get((uint32_t)aOperation, getter_AddRefs(operation)); + } + + NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER); + + nsLDAPMessage *msg = static_cast(aMsg); + msg->mOperation = operation; + + // proxy the listener callback to the ui thread. + RefPtr runnable = + new nsOnLDAPMessageRunnable(msg, aRemoveOpFromConnQ); + // invoke the callback + NS_DispatchToMainThread(runnable); + + // if requested (ie the operation is done), remove the operation + // from the connection queue. + if (aRemoveOpFromConnQ) + { + MutexAutoLock lock(mPendingOperationsMutex); + mPendingOperations.Remove(aOperation); + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("pending operation removed; total pending operations now =" + " %d\n", mPendingOperations.Count())); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPConnection::OnLookupComplete(nsICancelable *aRequest, + nsIDNSRecord *aRecord, + nsresult aStatus) +{ + nsresult rv = NS_OK; + + if (aRecord) { + // Build mResolvedIP list + // + mResolvedIP.Truncate(); + + int32_t index = 0; + nsCString addrbuf; + nsCOMPtr addr; + + while (NS_SUCCEEDED(aRecord->GetScriptableNextAddr(0, getter_AddRefs(addr)))) { + // We can only use v4 addresses + // + uint16_t family = 0; + bool v4mapped = false; + addr->GetFamily(&family); + addr->GetIsV4Mapped(&v4mapped); + if (family == nsINetAddr::FAMILY_INET || v4mapped) { + // If there are more IPs in the list, we separate them with + // a space, as supported/used by the LDAP C-SDK. + // + if (index++) + mResolvedIP.Append(' '); + + // Convert the IPv4 address to a string, and append it to our + // list of IPs. Strip leading '::FFFF:' (the IPv4-mapped-IPv6 + // indicator) if present. + // + addr->GetAddress(addrbuf); + if (addrbuf[0] == ':' && addrbuf.Length() > 7) + mResolvedIP.Append(Substring(addrbuf, 7)); + else + mResolvedIP.Append(addrbuf); + } + } + } + + if (NS_FAILED(aStatus)) { + // The DNS service failed, lets pass something reasonable + // back to the listener. + // + switch (aStatus) { + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_UNKNOWN_HOST: + case NS_ERROR_FAILURE: + case NS_ERROR_OFFLINE: + rv = aStatus; + break; + + default: + rv = NS_ERROR_UNEXPECTED; + break; + } + } else if (!mResolvedIP.Length()) { + // We have no host resolved, that is very bad, and should most + // likely have been caught earlier. + // + NS_ERROR("nsLDAPConnection::OnStopLookup(): the resolved IP " + "string is empty.\n"); + + rv = NS_ERROR_UNKNOWN_HOST; + } else { + // We've got the IP(s) for the hostname, now lets setup the + // LDAP connection using this information. Note that if the + // LDAP server returns a referral, the C-SDK will perform a + // new, synchronous DNS lookup, which might hang (but hopefully + // if we've come this far, DNS is working properly). + // + mConnectionHandle = ldap_init(mResolvedIP.get(), + mPort == -1 ? + (mSSL ? LDAPS_PORT : LDAP_PORT) : mPort); + // Check that we got a proper connection, and if so, setup the + // threading functions for this connection. + // + if ( !mConnectionHandle ) { + rv = NS_ERROR_FAILURE; // LDAP C SDK API gives no useful error + } else { +#if defined(DEBUG_dmose) || defined(DEBUG_bienvenu) + const int lDebug = 0; + ldap_set_option(mConnectionHandle, LDAP_OPT_DEBUG_LEVEL, &lDebug); +#endif + + // the C SDK currently defaults to v2. if we're to use v3, + // tell it so. + // + int version; + switch (mVersion) { + case 2: + break; + case 3: + version = LDAP_VERSION3; + ldap_set_option(mConnectionHandle, LDAP_OPT_PROTOCOL_VERSION, + &version); + break; + default: + NS_ERROR("nsLDAPConnection::OnLookupComplete(): mVersion" + " invalid"); + } + + // This code sets up the current connection to use PSM for SSL + // functionality. Making this use libssldap instead for + // non-browser user shouldn't be hard. + + extern nsresult nsLDAPInstallSSL(LDAP *ld, const char *aHostName); + + if (mSSL) { + if (ldap_set_option(mConnectionHandle, LDAP_OPT_SSL, + LDAP_OPT_ON) != LDAP_SUCCESS ) { + NS_ERROR("nsLDAPConnection::OnStopLookup(): Error" + " configuring connection to use SSL"); + rv = NS_ERROR_UNEXPECTED; + } + + rv = nsLDAPInstallSSL(mConnectionHandle, mDNSHost.get()); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPConnection::OnStopLookup(): Error" + " installing secure LDAP routines for" + " connection"); + } + } + } + } + + // Drop the DNS request object, we no longer need it, and set the flag + // indicating that DNS has finished. + // + mDNSRequest = nullptr; + mDNSHost.Truncate(); + + // Call the listener, and then we can release our reference to it. + // + mInitListener->OnLDAPInit(this, rv); + mInitListener = nullptr; + + return rv; +} + +nsLDAPConnectionRunnable::nsLDAPConnectionRunnable(int32_t aOperationID, + nsILDAPOperation *aOperation, + nsLDAPConnection *aConnection) + : mOperationID(aOperationID), mConnection(aConnection) +{ +} + +nsLDAPConnectionRunnable::~nsLDAPConnectionRunnable() +{ + if (mConnection) { + NS_ReleaseOnMainThread(mConnection.forget()); + } +} + +NS_IMPL_ISUPPORTS(nsLDAPConnectionRunnable, nsIRunnable) + +NS_IMETHODIMP nsLDAPConnectionRunnable::Run() +{ + if (!mOperationID) { + NS_ERROR("mOperationID is null"); + return NS_ERROR_NULL_POINTER; + } + + LDAPMessage *msgHandle; + bool operationFinished = true; + RefPtr msg; + + struct timeval timeout = { 0, 0 }; + + nsCOMPtr thread = do_GetCurrentThread(); + int32_t returnCode = ldap_result(mConnection->mConnectionHandle, mOperationID, LDAP_MSG_ONE, &timeout, &msgHandle); + switch (returnCode) + { + // timeout + case 0: + // XXX do we need a timer? + return thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + case -1: + NS_ERROR("We don't know what went wrong with the ldap operation"); + return NS_ERROR_FAILURE; + + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + // XXX what should we do with LDAP_RES_SEARCH_EXTENDED + operationFinished = false; + MOZ_FALLTHROUGH; + default: + { + msg = new nsLDAPMessage; + if (!msg) + return NS_ERROR_NULL_POINTER; + + // initialize the message, using a protected method not available + // through nsILDAPMessage (which is why we need the raw pointer) + nsresult rv = msg->Init(mConnection, msgHandle); + + switch (rv) + { + case NS_OK: + { + int32_t errorCode; + msg->GetErrorCode(&errorCode); + + // maybe a version error, e.g., using v3 on a v2 server. + // if we're using v3, try v2. + if (errorCode == LDAP_PROTOCOL_ERROR && + mConnection->mVersion == nsILDAPConnection::VERSION3) + { + nsAutoCString password; + mConnection->mVersion = nsILDAPConnection::VERSION2; + ldap_set_option(mConnection->mConnectionHandle, + LDAP_OPT_PROTOCOL_VERSION, &mConnection->mVersion); + + if (NS_SUCCEEDED(rv)) + { + // We don't want to notify callers that we are done, so + // redispatch the runnable. + // XXX do we need a timer? + rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; + } + } + // If we're midway through a SASL Bind, we need to continue + // without letting our caller know what we're up to! + // + if (errorCode == LDAP_SASL_BIND_IN_PROGRESS) { + struct berval *creds; + ldap_parse_sasl_bind_result( + mConnection->mConnectionHandle, msgHandle, + &creds, 0); + + nsCOMPtr operation; + { + MutexAutoLock lock(mConnection->mPendingOperationsMutex); + mConnection->mPendingOperations.Get((uint32_t)mOperationID, getter_AddRefs(operation)); + } + + NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER); + + nsresult rv = operation->SaslStep(creds->bv_val, creds->bv_len); + if (NS_SUCCEEDED(rv)) + return NS_OK; + } + break; + } + // Error code handling in here + default: + return NS_OK; + } + + // invoke the callback on the nsILDAPOperation corresponding to + // this message + rv = mConnection->InvokeMessageCallback(msgHandle, msg, mOperationID, + operationFinished); + if (NS_FAILED(rv)) + { + NS_ERROR("CheckLDAPOperationResult(): error invoking message" + " callback"); + // punt and hope things work out better next time around + return NS_OK; + } + + if (!operationFinished) + { + // XXX do we need a timer? + rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } + + break; + } + } + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPConnection.h b/ldap/xpcom/src/nsLDAPConnection.h new file mode 100644 index 0000000000..08c88295cd --- /dev/null +++ b/ldap/xpcom/src/nsLDAPConnection.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsLDAPConnection_h_ +#define _nsLDAPConnection_h_ + +#include "nsILDAPConnection.h" +#include "ldap.h" +#include "nsStringGlue.h" +#include "nsIThread.h" +#include "nsIRunnable.h" +#include "nsCOMPtr.h" +#include "nsILDAPMessageListener.h" +#include "nsInterfaceHashtable.h" +#include "nspr.h" +#include "nsWeakReference.h" +#include "nsWeakPtr.h" +#include "nsIDNSListener.h" +#include "nsICancelable.h" +#include "nsIRequest.h" +#include "nsCOMArray.h" +#include "nsIObserver.h" +#include "nsAutoPtr.h" +#include "mozilla/Mutex.h" + +/** + * Casting nsILDAPConnection to nsISupports is ambiguous. + * This method handles that. + */ +inline nsISupports* +ToSupports(nsILDAPConnection* p) +{ + return NS_ISUPPORTS_CAST(nsILDAPConnection*, p); +} + +// 0d871e30-1dd2-11b2-8ea9-831778c78e93 +// +#define NS_LDAPCONNECTION_CID \ +{ 0x0d871e30, 0x1dd2, 0x11b2, \ + { 0x8e, 0xa9, 0x83, 0x17, 0x78, 0xc7, 0x8e, 0x93 }} + +class nsLDAPConnection : public nsILDAPConnection, + public nsSupportsWeakReference, + public nsIDNSListener, + public nsIObserver + +{ + friend class nsLDAPOperation; + friend class nsLDAPMessage; + friend class nsLDAPConnectionRunnable; + typedef mozilla::Mutex Mutex; + + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPCONNECTION + NS_DECL_NSIDNSLISTENER + NS_DECL_NSIOBSERVER + + // constructor & destructor + // + nsLDAPConnection(); + + protected: + virtual ~nsLDAPConnection(); + // invoke the callback associated with a given message, and possibly + // delete it from the connection queue + // + nsresult InvokeMessageCallback(LDAPMessage *aMsgHandle, + nsILDAPMessage *aMsg, + int32_t aOperation, + bool aRemoveOpFromConnQ); + /** + * Add an nsILDAPOperation to the list of operations pending on + * this connection. This is mainly intended for use by the + * nsLDAPOperation code. Used so that the thread waiting on messages + * for this connection has an operation to callback to. + * + * @param aOperation operation to add + * @exception NS_ERROR_ILLEGAL_VALUE aOperation was NULL + * @exception NS_ERROR_UNEXPECTED this operation's msgId was not + * unique to this connection + * @exception NS_ERROR_OUT_OF_MEMORY out of memory + */ + nsresult AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation); + + /** + * Remove an nsILDAPOperation from the list of operations pending on this + * connection. Mainly intended for use by the nsLDAPOperation code. + * + * @param aOperation operation to add + * @exception NS_ERROR_INVALID_POINTER aOperation was NULL + * @exception NS_ERROR_OUT_OF_MEMORY out of memory + * @exception NS_ERROR_FAILURE could not delete the operation + */ + nsresult RemovePendingOperation(uint32_t aOperationID); + + void Close(); // close the connection + LDAP *mConnectionHandle; // the LDAP C SDK's connection object + nsCString mBindName; // who to bind as + nsCOMPtr mThread; // thread which marshals results + + Mutex mPendingOperationsMutex; + nsInterfaceHashtable mPendingOperations; + + int32_t mPort; // The LDAP port we're binding to + bool mSSL; // the options + uint32_t mVersion; // LDAP protocol version + + nsCString mResolvedIP; // Preresolved list of host IPs + nsCOMPtr mInitListener; // Init callback + nsCOMPtr mDNSRequest; // The "active" DNS request + nsCString mDNSHost; // The hostname being resolved + nsCOMPtr mClosure; // private parameter (anything caller desires) +}; + +class nsLDAPConnectionRunnable : public nsIRunnable +{ + friend class nsLDAPConnection; + friend class nsLDAPMessage; + +public: + nsLDAPConnectionRunnable(int32_t aOperationID, + nsILDAPOperation *aOperation, + nsLDAPConnection *aConnection); + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + + int32_t mOperationID; + RefPtr mConnection; + +private: + virtual ~nsLDAPConnectionRunnable(); +}; + +#endif // _nsLDAPConnection_h_ diff --git a/ldap/xpcom/src/nsLDAPControl.cpp b/ldap/xpcom/src/nsLDAPControl.cpp new file mode 100644 index 0000000000..a019728355 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPControl.cpp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPControl.h" +#include "prmem.h" +#include "plstr.h" +#include "nsLDAPBERValue.h" + +NS_IMPL_ISUPPORTS(nsLDAPControl, nsILDAPControl) + +nsLDAPControl::nsLDAPControl() + : mIsCritical(false) +{ +} + +nsLDAPControl::~nsLDAPControl() +{ +} + +/* attribute ACString oid; */ +NS_IMETHODIMP nsLDAPControl::GetOid(nsACString & aOid) +{ + aOid.Assign(mOid); + return NS_OK; +} +NS_IMETHODIMP nsLDAPControl::SetOid(const nsACString & aOid) +{ + mOid = aOid; + return NS_OK; +} + +/* attribute nsILDAPBERValue value; */ +NS_IMETHODIMP +nsLDAPControl::GetValue(nsILDAPBERValue * *aValue) +{ + NS_IF_ADDREF(*aValue = mValue); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPControl::SetValue(nsILDAPBERValue * aValue) +{ + mValue = aValue; + return NS_OK; +} + +/* attribute boolean isCritical; */ +NS_IMETHODIMP +nsLDAPControl::GetIsCritical(bool *aIsCritical) +{ + *aIsCritical = mIsCritical; + return NS_OK; +} +NS_IMETHODIMP +nsLDAPControl::SetIsCritical(bool aIsCritical) +{ + mIsCritical = aIsCritical; + return NS_OK; +} + +/** + * utility routine for use inside the LDAP XPCOM SDK + */ +nsresult +nsLDAPControl::ToLDAPControl(LDAPControl **control) +{ + // because nsLDAPProtocolModule::Init calls prldap_install_routines we know + // that the C SDK will be using the NSPR allocator under the hood, so our + // callers will therefore be able to use ldap_control_free() and friends on + // this control. + LDAPControl *ctl = static_cast(PR_Calloc(1, sizeof(LDAPControl))); + if (!ctl) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // need to ensure that this string is also alloced by PR_Alloc + ctl->ldctl_oid = PL_strdup(mOid.get()); + if (!ctl->ldctl_oid) { + PR_Free(ctl); + return NS_ERROR_OUT_OF_MEMORY; + } + + ctl->ldctl_iscritical = mIsCritical; + + if (!mValue) { + // no data associated with this control + ctl->ldctl_value.bv_len = 0; + ctl->ldctl_value.bv_val = 0; + } else { + + // just to make the code below a bit more readable + nsLDAPBERValue *nsBerVal = + static_cast(static_cast + (mValue.get())); + ctl->ldctl_value.bv_len = nsBerVal->mSize; + + if (!nsBerVal->mSize) { + // a zero-length value is associated with this control + return NS_ERROR_NOT_IMPLEMENTED; + } else { + + // same for the berval itself + ctl->ldctl_value.bv_len = nsBerVal->mSize; + ctl->ldctl_value.bv_val = static_cast + (PR_Malloc(nsBerVal->mSize)); + if (!ctl->ldctl_value.bv_val) { + ldap_control_free(ctl); + return NS_ERROR_OUT_OF_MEMORY; + } + + memcpy(ctl->ldctl_value.bv_val, nsBerVal->mValue, + ctl->ldctl_value.bv_len); + } + } + + *control = ctl; + + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPControl.h b/ldap/xpcom/src/nsLDAPControl.h new file mode 100644 index 0000000000..6baf269c71 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPControl.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsILDAPControl.h" +#include "nsCOMPtr.h" +#include "nsILDAPBERValue.h" +#include "nsStringGlue.h" +#include "ldap.h" + +// {5B608BBE-C0EA-4f74-B209-9CDCD79EC401} +#define NS_LDAPCONTROL_CID \ + { 0x5b608bbe, 0xc0ea, 0x4f74, \ + { 0xb2, 0x9, 0x9c, 0xdc, 0xd7, 0x9e, 0xc4, 0x1 } } + +class nsLDAPControl final : public nsILDAPControl +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSILDAPCONTROL + + nsLDAPControl(); + + /** + * return a pointer to C-SDK compatible LDAPControl structure. Note that + * this is allocated with NS_Alloc and must be freed with NS_Free, both by + * ldap_control_free() and friends. + * + * @exception null pointer return if allocation failed + */ + nsresult ToLDAPControl(LDAPControl **aControl); + +private: + ~nsLDAPControl(); + +protected: + nsCOMPtr mValue; // the value portion of this control + bool mIsCritical; // should server abort if control not understood? + nsCString mOid; // Object ID for this control +}; diff --git a/ldap/xpcom/src/nsLDAPInternal.h b/ldap/xpcom/src/nsLDAPInternal.h new file mode 100644 index 0000000000..989f113b32 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPInternal.h @@ -0,0 +1,11 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prlog.h" + +#ifdef PR_LOGGING +extern PRLogModuleInfo *gLDAPLogModule; // defn in nsLDAPProtocolModule.cpp +#endif diff --git a/ldap/xpcom/src/nsLDAPMessage.cpp b/ldap/xpcom/src/nsLDAPMessage.cpp new file mode 100644 index 0000000000..782fa047ff --- /dev/null +++ b/ldap/xpcom/src/nsLDAPMessage.cpp @@ -0,0 +1,649 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPInternal.h" +#include "nsLDAPMessage.h" +#include "nspr.h" +#include "nsDebug.h" +#include "nsMemory.h" +#include "nsLDAPConnection.h" +#include "nsISupportsUtils.h" +#include "nsLDAPBERValue.h" +#include "nsILDAPErrors.h" +#include "nsIClassInfoImpl.h" +#include "nsLDAPUtils.h" +#include "mozilla/Logging.h" + +NS_IMPL_CLASSINFO(nsLDAPMessage, NULL, nsIClassInfo::THREADSAFE, + NS_LDAPMESSAGE_CID) + +NS_IMPL_ADDREF(nsLDAPMessage) +NS_IMPL_RELEASE(nsLDAPMessage) +NS_INTERFACE_MAP_BEGIN(nsLDAPMessage) + NS_INTERFACE_MAP_ENTRY(nsILDAPMessage) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPMessage) + NS_IMPL_QUERY_CLASSINFO(nsLDAPMessage) +NS_INTERFACE_MAP_END_THREADSAFE +NS_IMPL_CI_INTERFACE_GETTER(nsLDAPMessage, nsILDAPMessage) + + +// constructor +// +nsLDAPMessage::nsLDAPMessage() + : mMsgHandle(0), + mErrorCode(LDAP_SUCCESS), + mMatchedDn(0), + mErrorMessage(0), + mReferrals(0), + mServerControls(0) +{ +} + +// destructor +// +nsLDAPMessage::~nsLDAPMessage(void) +{ + if (mMsgHandle) { + int rc = ldap_msgfree(mMsgHandle); + +// If you are having problems compiling the following code on a Solaris +// machine with the Forte 6 Update 1 compilers, then you need to make +// sure you have applied all the required patches. See: +// http://www.mozilla.org/unix/solaris-build.html for more details. + + switch(rc) { + case LDAP_RES_BIND: + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_RESULT: + case LDAP_RES_MODIFY: + case LDAP_RES_ADD: + case LDAP_RES_DELETE: + case LDAP_RES_MODRDN: + case LDAP_RES_COMPARE: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_EXTENDED: + case LDAP_RES_ANY: + // success + break; + + case LDAP_SUCCESS: + // timed out (dunno why LDAP_SUCCESS is used to indicate this) + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() " + "timed out\n")); + break; + + default: + // other failure + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() " + "failed: %s\n", ldap_err2string(rc))); + break; + } + } + + if (mMatchedDn) { + ldap_memfree(mMatchedDn); + } + + if (mErrorMessage) { + ldap_memfree(mErrorMessage); + } + + if (mReferrals) { + ldap_value_free(mReferrals); + } + + if (mServerControls) { + ldap_controls_free(mServerControls); + } + +} + +/** + * Initializes a message. + * + * @param aConnection The nsLDAPConnection this message is on + * @param aMsgHandle The native LDAPMessage to be wrapped. + * + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + * @exception NS_ERROR_UNEXPECTED internal err; shouldn't happen + * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER decoding + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ +nsresult +nsLDAPMessage::Init(nsILDAPConnection *aConnection, LDAPMessage *aMsgHandle) +{ + int parseResult; + + if (!aConnection || !aMsgHandle) { + NS_WARNING("Null pointer passed in to nsLDAPMessage::Init()"); + return NS_ERROR_ILLEGAL_VALUE; + } + + // initialize the appropriate member vars + // + mConnection = aConnection; + mMsgHandle = aMsgHandle; + + // cache the connection handle. we're violating the XPCOM type-system + // here since we're a friend of the connection class and in the + // same module. + // + mConnectionHandle = static_cast(aConnection)->mConnectionHandle; + + // do any useful message parsing + // + const int msgType = ldap_msgtype(mMsgHandle); + if ( msgType == -1) { + NS_ERROR("nsLDAPMessage::Init(): ldap_msgtype() failed"); + return NS_ERROR_UNEXPECTED; + } + + switch (msgType) { + + case LDAP_RES_SEARCH_REFERENCE: + // XXX should do something here? + break; + + case LDAP_RES_SEARCH_ENTRY: + // nothing to do here + break; + + case LDAP_RES_EXTENDED: + // XXX should do something here? + break; + + case LDAP_RES_BIND: + case LDAP_RES_SEARCH_RESULT: + case LDAP_RES_MODIFY: + case LDAP_RES_ADD: + case LDAP_RES_DELETE: + case LDAP_RES_MODRDN: + case LDAP_RES_COMPARE: + parseResult = ldap_parse_result(mConnectionHandle, + mMsgHandle, &mErrorCode, &mMatchedDn, + &mErrorMessage,&mReferrals, + &mServerControls, 0); + switch (parseResult) { + case LDAP_SUCCESS: + // we're good + break; + + case LDAP_DECODING_ERROR: + NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() hit a " + "decoding error"); + return NS_ERROR_LDAP_DECODING_ERROR; + + case LDAP_NO_MEMORY: + NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() ran out " + "of memory"); + return NS_ERROR_OUT_OF_MEMORY; + + case LDAP_PARAM_ERROR: + case LDAP_MORE_RESULTS_TO_RETURN: + case LDAP_NO_RESULTS_RETURNED: + default: + NS_ERROR("nsLDAPMessage::Init(): ldap_parse_result returned " + "unexpected return code"); + return NS_ERROR_UNEXPECTED; + } + + break; + + default: + NS_ERROR("nsLDAPMessage::Init(): unexpected message type"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +/** + * The result code of the (possibly partial) operation. + * + * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in + * + * readonly attribute long errorCode; + */ +NS_IMETHODIMP +nsLDAPMessage::GetErrorCode(int32_t *aErrorCode) +{ + if (!aErrorCode) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aErrorCode = mErrorCode; + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPMessage::GetType(int32_t *aType) +{ + if (!aType) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aType = ldap_msgtype(mMsgHandle); + if (*aType == -1) { + return NS_ERROR_UNEXPECTED; + }; + + return NS_OK; +} + +// we don't get to use exceptions, so we'll fake it. this is an error +// handler for IterateAttributes(). +// +nsresult +nsLDAPMessage::IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount, + char** *aAttributes, BerElement *position) +{ + + // if necessary, free the position holder used by + // ldap_{first,next}_attribute() + // + if (position) { + ldap_ber_free(position, 0); + } + + // deallocate any entries in the array that have been allocated, then + // the array itself + // + if (*aAttributes) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aAttrCount, *aAttributes); + } + + // possibly spit out a debugging message, then return an appropriate + // error code + // + switch (aLderrno) { + + case LDAP_PARAM_ERROR: + NS_WARNING("nsLDAPMessage::IterateAttributes() failure; probable bug " + "or memory corruption encountered"); + return NS_ERROR_UNEXPECTED; + break; + + case LDAP_DECODING_ERROR: + NS_WARNING("nsLDAPMessage::IterateAttributes(): decoding error"); + return NS_ERROR_LDAP_DECODING_ERROR; + break; + + case LDAP_NO_MEMORY: + return NS_ERROR_OUT_OF_MEMORY; + break; + + } + + NS_WARNING("nsLDAPMessage::IterateAttributes(): LDAP C SDK returned " + "unexpected value; possible bug or memory corruption"); + return NS_ERROR_UNEXPECTED; +} + + +// wrapper for ldap_first_attribute +// +NS_IMETHODIMP +nsLDAPMessage::GetAttributes(uint32_t *aAttrCount, char** *aAttributes) +{ + return IterateAttributes(aAttrCount, aAttributes, true); +} + +// if getP is true, we get the attributes by recursing once +// (without getP set) in order to fill in *attrCount, then allocate +// and fill in the *aAttributes. +// +// if getP is false, just fill in *attrCount and return +// +nsresult +nsLDAPMessage::IterateAttributes(uint32_t *aAttrCount, char** *aAttributes, + bool getP) +{ + BerElement *position; + nsresult rv; + + if (!aAttrCount || !aAttributes ) { + return NS_ERROR_INVALID_POINTER; + } + + // if we've been called from GetAttributes, recurse once in order to + // count the elements in this message. + // + if (getP) { + *aAttributes = 0; + *aAttrCount = 0; + + rv = IterateAttributes(aAttrCount, aAttributes, false); + if (NS_FAILED(rv)) + return rv; + + // create an array of the appropriate size + // + *aAttributes = static_cast(moz_xmalloc(*aAttrCount * + sizeof(char *))); + if (!*aAttributes) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + // get the first attribute + // + char *attr = ldap_first_attribute(mConnectionHandle, + mMsgHandle, + &position); + if (!attr) { + return IterateAttrErrHandler(ldap_get_lderrno(mConnectionHandle, 0, 0), + aAttrCount, aAttributes, position); + } + + // if we're getting attributes, try and fill in the first field + // + if (getP) { + (*aAttributes)[0] = NS_strdup(attr); + if (!(*aAttributes)[0]) { + ldap_memfree(attr); + free(*aAttributes); + return NS_ERROR_OUT_OF_MEMORY; + } + + // note that we start counting again, in order to keep our place in + // the array so that we can unwind gracefully and avoid leakage if + // we hit an error as we're filling in the array + // + *aAttrCount = 1; + } else { + + // otherwise just update the count + // + *aAttrCount = 1; + } + ldap_memfree(attr); + + while (1) { + + // get the next attribute + // + attr = ldap_next_attribute(mConnectionHandle, mMsgHandle, position); + + // check to see if there is an error, or if we're just done iterating + // + if (!attr) { + + // bail out if there's an error + // + int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); + if (lderrno != LDAP_SUCCESS) { + return IterateAttrErrHandler(lderrno, aAttrCount, aAttributes, + position); + } + + // otherwise, there are no more attributes; we're done with + // the while loop + // + break; + + } else if (getP) { + + // if ldap_next_attribute did return successfully, and + // we're supposed to fill in a value, do so. + // + (*aAttributes)[*aAttrCount] = NS_strdup(attr); + if (!(*aAttributes)[*aAttrCount]) { + ldap_memfree(attr); + return IterateAttrErrHandler(LDAP_NO_MEMORY, aAttrCount, + aAttributes, position); + } + + } + ldap_memfree(attr); + + // we're done using *aAttrCount as a c-style array index (ie starting + // at 0). update it to reflect the number of elements now in the array + // + *aAttrCount += 1; + } + + // free the position pointer, if necessary + // + if (position) { + ldap_ber_free(position, 0); + } + + return NS_OK; +} + +// readonly attribute wstring dn; +NS_IMETHODIMP nsLDAPMessage::GetDn(nsACString& aDn) +{ + char *rawDn = ldap_get_dn(mConnectionHandle, mMsgHandle); + + if (!rawDn) { + int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); + + switch (lderrno) { + + case LDAP_DECODING_ERROR: + NS_WARNING("nsLDAPMessage::GetDn(): ldap decoding error"); + return NS_ERROR_LDAP_DECODING_ERROR; + + case LDAP_PARAM_ERROR: + default: + NS_ERROR("nsLDAPMessage::GetDn(): internal error"); + return NS_ERROR_UNEXPECTED; + } + } + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPMessage::GetDn(): dn = '%s'", rawDn)); + + aDn.Assign(rawDn); + ldap_memfree(rawDn); + + return NS_OK; +} + +// wrapper for ldap_get_values() +// +NS_IMETHODIMP +nsLDAPMessage::GetValues(const char *aAttr, uint32_t *aCount, + char16_t ***aValues) +{ + char **values; + +#if defined(DEBUG) + // We only want this being logged for debug builds so as not to affect performance too much. + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPMessage::GetValues(): called with aAttr = '%s'", aAttr)); +#endif + + values = ldap_get_values(mConnectionHandle, mMsgHandle, aAttr); + + // bail out if there was a problem + // + if (!values) { + int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); + + if ( lderrno == LDAP_DECODING_ERROR ) { + // this may not be an error; it could just be that the + // caller has asked for an attribute that doesn't exist. + // + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPMessage::GetValues(): ldap_get_values returned " + "LDAP_DECODING_ERROR")); + return NS_ERROR_LDAP_DECODING_ERROR; + + } else if ( lderrno == LDAP_PARAM_ERROR ) { + NS_ERROR("nsLDAPMessage::GetValues(): internal error: 1"); + return NS_ERROR_UNEXPECTED; + + } else { + NS_ERROR("nsLDAPMessage::GetValues(): internal error: 2"); + return NS_ERROR_UNEXPECTED; + } + } + + // count the values + // + uint32_t numVals = ldap_count_values(values); + + // create an array of the appropriate size + // + *aValues = static_cast(moz_xmalloc(numVals * sizeof(char16_t *))); + if (!*aValues) { + ldap_value_free(values); + return NS_ERROR_OUT_OF_MEMORY; + } + + // clone the array (except for the trailing NULL entry) using the + // shared allocator for XPCOM correctness + // + uint32_t i; + for ( i = 0 ; i < numVals ; i++ ) { + nsDependentCString sValue(values[i]); + if (IsUTF8(sValue)) + (*aValues)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(sValue)); + else + (*aValues)[i] = ToNewUnicode(NS_ConvertASCIItoUTF16(sValue)); + if ( ! (*aValues)[i] ) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues); + ldap_value_free(values); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + // now free our value array since we already cloned the values array + // to the 'aValues' results array. + ldap_value_free(values); + + *aCount = numVals; + return NS_OK; +} + +// wrapper for get_values_len +// +NS_IMETHODIMP +nsLDAPMessage::GetBinaryValues(const char *aAttr, uint32_t *aCount, + nsILDAPBERValue ***aValues) +{ + struct berval **values; + +#if defined(DEBUG) + // We only want this being logged for debug builds so as not to affect performance too much. + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPMessage::GetBinaryValues(): called with aAttr = '%s'", + aAttr)); +#endif + + values = ldap_get_values_len(mConnectionHandle, mMsgHandle, aAttr); + + // bail out if there was a problem + // + if (!values) { + int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0); + + if ( lderrno == LDAP_DECODING_ERROR ) { + // this may not be an error; it could just be that the + // caller has asked for an attribute that doesn't exist. + // + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning, + ("nsLDAPMessage::GetBinaryValues(): ldap_get_values " + "returned LDAP_DECODING_ERROR")); + return NS_ERROR_LDAP_DECODING_ERROR; + + } else if ( lderrno == LDAP_PARAM_ERROR ) { + NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 1"); + return NS_ERROR_UNEXPECTED; + + } else { + NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 2"); + return NS_ERROR_UNEXPECTED; + } + } + + // count the values + // + uint32_t numVals = ldap_count_values_len(values); + + // create the out array + // + *aValues = + static_cast(moz_xmalloc(numVals * sizeof(nsILDAPBERValue))); + if (!aValues) { + ldap_value_free_len(values); + return NS_ERROR_OUT_OF_MEMORY; + } + + // clone the array (except for the trailing NULL entry) using the + // shared allocator for XPCOM correctness + // + uint32_t i; + nsresult rv; + for ( i = 0 ; i < numVals ; i++ ) { + + // create an nsBERValue object + // + nsCOMPtr berValue = new nsLDAPBERValue(); + if (!berValue) { + NS_ERROR("nsLDAPMessage::GetBinaryValues(): out of memory" + " creating nsLDAPBERValue object"); + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues); + ldap_value_free_len(values); + return NS_ERROR_OUT_OF_MEMORY; + } + + // copy the value from the struct into the nsBERValue + // + rv = berValue->Set(values[i]->bv_len, + reinterpret_cast(values[i]->bv_val)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPMessage::GetBinaryValues(): error setting" + " nsBERValue"); + ldap_value_free_len(values); + return rv == NS_ERROR_OUT_OF_MEMORY ? rv : NS_ERROR_UNEXPECTED; + } + + // put the nsIBERValue object into the out array + // + NS_ADDREF( (*aValues)[i] = berValue.get() ); + } + + *aCount = numVals; + ldap_value_free_len(values); + return NS_OK; +} + +// readonly attribute nsILDAPOperation operation; +NS_IMETHODIMP nsLDAPMessage::GetOperation(nsILDAPOperation **_retval) +{ + if (!_retval) { + NS_ERROR("nsLDAPMessage::GetOperation: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + NS_IF_ADDREF(*_retval = mOperation); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPMessage::ToUnicode(char16_t* *aString) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsLDAPMessage::GetErrorMessage(nsACString & aErrorMessage) +{ + aErrorMessage.Assign(mErrorMessage); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPMessage::GetMatchedDn(nsACString & aMatchedDn) +{ + aMatchedDn.Assign(mMatchedDn); + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPMessage.h b/ldap/xpcom/src/nsLDAPMessage.h new file mode 100644 index 0000000000..d3c0b2d2e2 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPMessage.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsLDAPMessage_h_ +#define _nsLDAPMessage_h_ + +#include "ldap.h" +#include "nsILDAPMessage.h" +#include "nsILDAPOperation.h" +#include "nsCOMPtr.h" + +// 76e061ad-a59f-43b6-b812-ee6e8e69423f +// +#define NS_LDAPMESSAGE_CID \ +{ 0x76e061ad, 0xa59f, 0x43b6, \ + { 0xb8, 0x12, 0xee, 0x6e, 0x8e, 0x69, 0x42, 0x3f }} + +class nsLDAPMessage : public nsILDAPMessage +{ + friend class nsLDAPOperation; + friend class nsLDAPConnection; + friend class nsLDAPConnectionRunnable; + friend class nsOnLDAPMessageRunnable; + + public: + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPMESSAGE + + // constructor & destructor + // + nsLDAPMessage(); + + protected: + virtual ~nsLDAPMessage(); + + nsresult IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount, + char** *aAttributes, BerElement *position); + nsresult IterateAttributes(uint32_t *aAttrCount, char** *aAttributes, + bool getP); + nsresult Init(nsILDAPConnection *aConnection, + LDAPMessage *aMsgHandle); + LDAPMessage *mMsgHandle; // the message we're wrapping + nsCOMPtr mOperation; // operation this msg relates to + + LDAP *mConnectionHandle; // cached connection this op is on + + // since we're caching the connection handle (above), we need to + // hold an owning ref to the relevant nsLDAPConnection object as long + // as we're around + // + nsCOMPtr mConnection; + + // the next five member vars are returned by ldap_parse_result() + // + int mErrorCode; + char *mMatchedDn; + char *mErrorMessage; + char **mReferrals; + LDAPControl **mServerControls; +}; + +#endif // _nsLDAPMessage_h diff --git a/ldap/xpcom/src/nsLDAPModification.cpp b/ldap/xpcom/src/nsLDAPModification.cpp new file mode 100644 index 0000000000..77253835aa --- /dev/null +++ b/ldap/xpcom/src/nsLDAPModification.cpp @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPModification.h" +#include "nsILDAPBERValue.h" +#include "nsISimpleEnumerator.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsLDAPModification, nsILDAPModification) + +// constructor +// +nsLDAPModification::nsLDAPModification() + : mValuesLock("nsLDAPModification.mValuesLock") +{ +} + +// destructor +// +nsLDAPModification::~nsLDAPModification() +{ +} + +nsresult +nsLDAPModification::Init() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::GetOperation(int32_t *aOperation) +{ + NS_ENSURE_ARG_POINTER(aOperation); + + *aOperation = mOperation; + return NS_OK; +} + +NS_IMETHODIMP nsLDAPModification::SetOperation(int32_t aOperation) +{ + mOperation = aOperation; + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::GetType(nsACString& aType) +{ + aType.Assign(mType); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::SetType(const nsACString& aType) +{ + mType.Assign(aType); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::GetValues(nsIArray** aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + MutexAutoLock lock(mValuesLock); + + if (!mValues) + return NS_ERROR_NOT_INITIALIZED; + + NS_ADDREF(*aResult = mValues); + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::SetValues(nsIArray* aValues) +{ + NS_ENSURE_ARG_POINTER(aValues); + + MutexAutoLock lock(mValuesLock); + nsresult rv; + + if (!mValues) + mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + else + rv = mValues->Clear(); + + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr enumerator; + rv = aValues->Enumerate(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMoreElements; + rv = enumerator->HasMoreElements(&hasMoreElements); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr value; + + while (hasMoreElements) + { + rv = enumerator->GetNext(getter_AddRefs(value)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mValues->AppendElement(value, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = enumerator->HasMoreElements(&hasMoreElements); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPModification::SetUpModification(int32_t aOperation, + const nsACString &aType, + nsIArray *aValues) +{ + // Set the values using our local function before entering lock + // to avoid deadlocks due to holding the same lock twice. + nsresult rv = SetValues(aValues); + + MutexAutoLock lock(mValuesLock); + + mOperation = aOperation; + mType.Assign(aType); + + return rv; +} + +NS_IMETHODIMP +nsLDAPModification::SetUpModificationOneValue(int32_t aOperation, + const nsACString &aType, + nsILDAPBERValue *aValue) +{ + NS_ENSURE_ARG_POINTER(aValue); + + MutexAutoLock lock(mValuesLock); + + mOperation = aOperation; + mType.Assign(aType); + + nsresult rv; + + if (!mValues) + mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + else + rv = mValues->Clear(); + + NS_ENSURE_SUCCESS(rv, rv); + + return mValues->AppendElement(aValue, false); +} diff --git a/ldap/xpcom/src/nsLDAPModification.h b/ldap/xpcom/src/nsLDAPModification.h new file mode 100644 index 0000000000..e9c9c55990 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPModification.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsLDAPModification_h_ +#define _nsLDAPModification_h_ + +#include "nsILDAPModification.h" +#include "nsIMutableArray.h" +#include "nsStringGlue.h" +#include "nsCOMPtr.h" +#include "mozilla/Mutex.h" + +// 5b0f4d00-062e-11d6-a7f2-fc943c3c039c +// +#define NS_LDAPMODIFICATION_CID \ +{ 0x5b0f4d00, 0x062e, 0x11d6, \ + { 0xa7, 0xf2, 0xfc, 0x94, 0x3c, 0x3c, 0x03, 0x9c }} + +class nsLDAPModification : public nsILDAPModification +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPMODIFICATION + + // constructor & destructor + // + nsLDAPModification(); + + nsresult Init(); + +private: + virtual ~nsLDAPModification(); + + int32_t mOperation; + nsCString mType; + nsCOMPtr mValues; + mozilla::Mutex mValuesLock; +}; + +#endif // _nsLDAPModification_h_ diff --git a/ldap/xpcom/src/nsLDAPOperation.cpp b/ldap/xpcom/src/nsLDAPOperation.cpp new file mode 100644 index 0000000000..72cf32089e --- /dev/null +++ b/ldap/xpcom/src/nsLDAPOperation.cpp @@ -0,0 +1,975 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPInternal.h" +#include "nsLDAPOperation.h" +#include "nsLDAPBERValue.h" +#include "nsILDAPMessage.h" +#include "nsILDAPModification.h" +#include "nsIComponentManager.h" +#include "nspr.h" +#include "nsISimpleEnumerator.h" +#include "nsLDAPControl.h" +#include "nsILDAPErrors.h" +#include "nsIClassInfoImpl.h" +#include "nsIAuthModule.h" +#include "nsArrayUtils.h" +#include "nsMemory.h" +#include "mozilla/Logging.h" + +// Helper function +static nsresult TranslateLDAPErrorToNSError(const int ldapError) +{ + switch (ldapError) { + case LDAP_SUCCESS: + return NS_OK; + + case LDAP_ENCODING_ERROR: + return NS_ERROR_LDAP_ENCODING_ERROR; + + case LDAP_CONNECT_ERROR: + return NS_ERROR_LDAP_CONNECT_ERROR; + + case LDAP_SERVER_DOWN: + return NS_ERROR_LDAP_SERVER_DOWN; + + case LDAP_NO_MEMORY: + return NS_ERROR_OUT_OF_MEMORY; + + case LDAP_NOT_SUPPORTED: + return NS_ERROR_LDAP_NOT_SUPPORTED; + + case LDAP_PARAM_ERROR: + return NS_ERROR_INVALID_ARG; + + case LDAP_FILTER_ERROR: + return NS_ERROR_LDAP_FILTER_ERROR; + + default: + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, + ("TranslateLDAPErrorToNSError: " + "Do not know how to translate LDAP error: 0x%x", ldapError)); + return NS_ERROR_UNEXPECTED; + } +} + + +// constructor +nsLDAPOperation::nsLDAPOperation() +{ +} + +// destructor +nsLDAPOperation::~nsLDAPOperation() +{ +} + +NS_IMPL_CLASSINFO(nsLDAPOperation, NULL, nsIClassInfo::THREADSAFE, + NS_LDAPOPERATION_CID) + +NS_IMPL_ADDREF(nsLDAPOperation) +NS_IMPL_RELEASE(nsLDAPOperation) +NS_INTERFACE_MAP_BEGIN(nsLDAPOperation) + NS_INTERFACE_MAP_ENTRY(nsILDAPOperation) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPOperation) + NS_IMPL_QUERY_CLASSINFO(nsLDAPOperation) +NS_INTERFACE_MAP_END_THREADSAFE +NS_IMPL_CI_INTERFACE_GETTER(nsLDAPOperation, nsILDAPOperation) + +/** + * Initializes this operation. Must be called prior to use. + * + * @param aConnection connection this operation should use + * @param aMessageListener where are the results are called back to. + */ +NS_IMETHODIMP +nsLDAPOperation::Init(nsILDAPConnection *aConnection, + nsILDAPMessageListener *aMessageListener, + nsISupports *aClosure) +{ + if (!aConnection) { + return NS_ERROR_ILLEGAL_VALUE; + } + + // so we know that the operation is not yet running (and therefore don't + // try and call ldap_abandon_ext() on it) or remove it from the queue. + // + mMsgID = 0; + + // set the member vars + // + mConnection = static_cast(aConnection); + mMessageListener = aMessageListener; + mClosure = aClosure; + + // cache the connection handle + // + mConnectionHandle = + mConnection->mConnectionHandle; + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::GetClosure(nsISupports **_retval) +{ + if (!_retval) { + return NS_ERROR_ILLEGAL_VALUE; + } + NS_IF_ADDREF(*_retval = mClosure); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::SetClosure(nsISupports *aClosure) +{ + mClosure = aClosure; + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::GetConnection(nsILDAPConnection* *aConnection) +{ + if (!aConnection) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aConnection = mConnection; + NS_IF_ADDREF(*aConnection); + + return NS_OK; +} + +void +nsLDAPOperation::Clear() +{ + mMessageListener = nullptr; + mClosure = nullptr; + mConnection = nullptr; +} + +NS_IMETHODIMP +nsLDAPOperation::GetMessageListener(nsILDAPMessageListener **aMessageListener) +{ + if (!aMessageListener) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aMessageListener = mMessageListener; + NS_IF_ADDREF(*aMessageListener); + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::SaslBind(const nsACString &service, + const nsACString &mechanism, + nsIAuthModule *authModule) +{ + nsresult rv; + nsAutoCString bindName; + struct berval creds; + unsigned int credlen; + + mAuthModule = authModule; + mMechanism.Assign(mechanism); + + rv = mConnection->GetBindName(bindName); + NS_ENSURE_SUCCESS(rv, rv); + + creds.bv_val = NULL; + mAuthModule->Init(PromiseFlatCString(service).get(), + nsIAuthModule::REQ_DEFAULT, nullptr, + NS_ConvertUTF8toUTF16(bindName).get(), nullptr); + + rv = mAuthModule->GetNextToken(nullptr, 0, (void **)&creds.bv_val, + &credlen); + if (NS_FAILED(rv) || !creds.bv_val) + return rv; + + creds.bv_len = credlen; + const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(), + mMechanism.get(), &creds, NULL, NULL, + &mMsgID); + free(creds.bv_val); + + if (lderrno != LDAP_SUCCESS) + return TranslateLDAPErrorToNSError(lderrno); + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + + if (NS_FAILED(rv)) + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + + return rv; +} + +NS_IMETHODIMP +nsLDAPOperation::SaslStep(const char *token, uint32_t tokenLen) +{ + nsresult rv; + nsAutoCString bindName; + struct berval clientCreds; + struct berval serverCreds; + unsigned int credlen; + + rv = mConnection->RemovePendingOperation(mMsgID); + NS_ENSURE_SUCCESS(rv, rv); + + serverCreds.bv_val = (char *) token; + serverCreds.bv_len = tokenLen; + + rv = mConnection->GetBindName(bindName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mAuthModule->GetNextToken(serverCreds.bv_val, serverCreds.bv_len, + (void **) &clientCreds.bv_val, &credlen); + NS_ENSURE_SUCCESS(rv, rv); + + clientCreds.bv_len = credlen; + + const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(), + mMechanism.get(), &clientCreds, NULL, + NULL, &mMsgID); + + free(clientCreds.bv_val); + + if (lderrno != LDAP_SUCCESS) + return TranslateLDAPErrorToNSError(lderrno); + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + if (NS_FAILED(rv)) + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + + return rv; +} + + +// wrapper for ldap_simple_bind() +// +NS_IMETHODIMP +nsLDAPOperation::SimpleBind(const nsACString& passwd) +{ + RefPtr connection = mConnection; + // There is a possibilty that mConnection can be cleared by another + // thread. Grabbing a local reference to mConnection may avoid this. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=557928#c1 + nsresult rv; + nsAutoCString bindName; + int32_t originalMsgID = mMsgID; + // Ugly hack alert: + // the first time we get called with a passwd, remember it. + // Then, if we get called again w/o a password, use the + // saved one. Getting called again means we're trying to + // fall back to VERSION2. + // Since LDAP operations are thrown away when done, it won't stay + // around in memory. + if (!passwd.IsEmpty()) + mSavePassword = passwd; + + NS_PRECONDITION(mMessageListener, "MessageListener not set"); + + rv = connection->GetBindName(bindName); + if (NS_FAILED(rv)) + return rv; + + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::SimpleBind(): called; bindName = '%s'; ", + bindName.get())); + + // this (nsLDAPOperation) may be released by RemovePendingOperation() + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1063829. + RefPtr kungFuDeathGrip = this; + + // If this is a second try at binding, remove the operation from pending ops + // because msg id has changed... + if (originalMsgID) + connection->RemovePendingOperation(originalMsgID); + + mMsgID = ldap_simple_bind(mConnectionHandle, bindName.get(), + mSavePassword.get()); + + if (mMsgID == -1) { + // XXX Should NS_ERROR_LDAP_SERVER_DOWN cause a rebind here? + return TranslateLDAPErrorToNSError(ldap_get_lderrno(mConnectionHandle, + 0, 0)); + } + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = connection->AddPendingOperation(mMsgID, this); + switch (rv) { + case NS_OK: + break; + + // note that the return value of ldap_abandon_ext() is ignored, as + // there's nothing useful to do with it + + case NS_ERROR_OUT_OF_MEMORY: + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + return NS_ERROR_OUT_OF_MEMORY; + break; + + case NS_ERROR_UNEXPECTED: + case NS_ERROR_ILLEGAL_VALUE: + default: + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +/** + * Given an nsIArray of nsILDAPControls, return the appropriate + * zero-terminated array of LDAPControls ready to pass in to the C SDK. + */ +static nsresult +convertControlArray(nsIArray *aXpcomArray, LDAPControl ***aArray) +{ + // get the size of the original array + uint32_t length; + nsresult rv = aXpcomArray->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + // don't allocate an array if someone passed us in an empty one + if (!length) { + *aArray = 0; + return NS_OK; + } + + // allocate a local array of the form understood by the C-SDK; + // +1 is to account for the final null terminator. PR_Calloc is + // is used so that ldap_controls_free will work anywhere during the + // iteration + LDAPControl **controls = + static_cast + (PR_Calloc(length+1, sizeof(LDAPControl))); + + // prepare to enumerate the array + nsCOMPtr enumerator; + rv = aXpcomArray->Enumerate(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + bool moreElements; + rv = enumerator->HasMoreElements(&moreElements); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t i = 0; + while (moreElements) { + + // get the next array element + nsCOMPtr isupports; + rv = enumerator->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) { + ldap_controls_free(controls); + return rv; + } + nsCOMPtr control = do_QueryInterface(isupports, &rv); + if (NS_FAILED(rv)) { + ldap_controls_free(controls); + return NS_ERROR_INVALID_ARG; // bogus element in the array + } + nsLDAPControl *ctl = static_cast + (static_cast + (control.get())); + + // convert it to an LDAPControl structure placed in the new array + rv = ctl->ToLDAPControl(&controls[i]); + if (NS_FAILED(rv)) { + ldap_controls_free(controls); + return rv; + } + + // on to the next element + rv = enumerator->HasMoreElements(&moreElements); + if (NS_FAILED(rv)) { + ldap_controls_free(controls); + return NS_ERROR_UNEXPECTED; + } + ++i; + } + + *aArray = controls; + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::SearchExt(const nsACString& aBaseDn, int32_t aScope, + const nsACString& aFilter, + const nsACString &aAttributes, + PRIntervalTime aTimeOut, int32_t aSizeLimit) +{ + if (!mMessageListener) { + NS_ERROR("nsLDAPOperation::SearchExt(): mMessageListener not set"); + return NS_ERROR_NOT_INITIALIZED; + } + + // XXX add control logging + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::SearchExt(): called with aBaseDn = '%s'; " + "aFilter = '%s'; aAttributes = %s; aSizeLimit = %d", + PromiseFlatCString(aBaseDn).get(), + PromiseFlatCString(aFilter).get(), + PromiseFlatCString(aAttributes).get(), aSizeLimit)); + + LDAPControl **serverctls = 0; + nsresult rv; + if (mServerControls) { + rv = convertControlArray(mServerControls, &serverctls); + if (NS_FAILED(rv)) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, + ("nsLDAPOperation::SearchExt(): error converting server " + "control array: %x", rv)); + return rv; + } + } + + LDAPControl **clientctls = 0; + if (mClientControls) { + rv = convertControlArray(mClientControls, &clientctls); + if (NS_FAILED(rv)) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, + ("nsLDAPOperation::SearchExt(): error converting client " + "control array: %x", rv)); + ldap_controls_free(serverctls); + return rv; + } + } + + // Convert our comma separated string to one that the C-SDK will like, i.e. + // convert to a char array and add a last NULL element. + nsTArray attrArray; + ParseString(aAttributes, ',', attrArray); + char **attrs = nullptr; + uint32_t origLength = attrArray.Length(); + if (origLength) + { + attrs = static_cast (NS_Alloc((origLength + 1) * sizeof(char *))); + if (!attrs) + return NS_ERROR_OUT_OF_MEMORY; + + for (uint32_t i = 0; i < origLength; ++i) + attrs[i] = ToNewCString(attrArray[i]); + + attrs[origLength] = 0; + } + + // XXX deal with timeout here + int retVal = ldap_search_ext(mConnectionHandle, + PromiseFlatCString(aBaseDn).get(), + aScope, PromiseFlatCString(aFilter).get(), + attrs, 0, serverctls, clientctls, 0, + aSizeLimit, &mMsgID); + + // clean up + ldap_controls_free(serverctls); + ldap_controls_free(clientctls); + // The last entry is null, so no need to free that. + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(origLength, attrs); + + rv = TranslateLDAPErrorToNSError(retVal); + NS_ENSURE_SUCCESS(rv, rv); + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + // + rv = mConnection->AddPendingOperation(mMsgID, this); + if (NS_FAILED(rv)) { + switch (rv) { + case NS_ERROR_OUT_OF_MEMORY: + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + return NS_ERROR_OUT_OF_MEMORY; + + default: + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + NS_ERROR("nsLDAPOperation::SearchExt(): unexpected error in " + "mConnection->AddPendingOperation"); + return NS_ERROR_UNEXPECTED; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::GetMessageID(int32_t *aMsgID) +{ + if (!aMsgID) { + return NS_ERROR_ILLEGAL_VALUE; + } + + *aMsgID = mMsgID; + + return NS_OK; +} + +// as far as I can tell from reading the LDAP C SDK code, abandoning something +// that has already been abandoned does not return an error +// +NS_IMETHODIMP +nsLDAPOperation::AbandonExt() +{ + nsresult rv; + nsresult retStatus = NS_OK; + + if (!mMessageListener || mMsgID == 0) { + NS_ERROR("nsLDAPOperation::AbandonExt(): mMessageListener or " + "mMsgId not initialized"); + return NS_ERROR_NOT_INITIALIZED; + } + + // XXX handle controls here + if (mServerControls || mClientControls) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + rv = TranslateLDAPErrorToNSError(ldap_abandon_ext(mConnectionHandle, + mMsgID, 0, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + // try to remove it from the pendingOperations queue, if it's there. + // even if something goes wrong here, the abandon() has already succeeded + // succeeded (and there's nothing else the caller can reasonably do), + // so we only pay attention to this in debug builds. + // + // check mConnection in case we're getting bit by + // http://bugzilla.mozilla.org/show_bug.cgi?id=239729, wherein we + // theorize that ::Clearing the operation is nulling out the mConnection + // from another thread. + if (mConnection) + { + rv = mConnection->RemovePendingOperation(mMsgID); + + if (NS_FAILED(rv)) { + // XXXdmose should we keep AbandonExt from happening on multiple + // threads at the same time? that's when this condition is most + // likely to occur. i _think_ the LDAP C SDK is ok with this; need + // to verify. + // + NS_WARNING("nsLDAPOperation::AbandonExt: " + "mConnection->RemovePendingOperation(this) failed."); + } + } + + return retStatus; +} + +NS_IMETHODIMP +nsLDAPOperation::GetClientControls(nsIMutableArray **aControls) +{ + NS_IF_ADDREF(*aControls = mClientControls); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPOperation::SetClientControls(nsIMutableArray *aControls) +{ + mClientControls = aControls; + return NS_OK; +} + +NS_IMETHODIMP nsLDAPOperation::GetServerControls(nsIMutableArray **aControls) +{ + NS_IF_ADDREF(*aControls = mServerControls); + return NS_OK; +} + +NS_IMETHODIMP nsLDAPOperation::SetServerControls(nsIMutableArray *aControls) +{ + mServerControls = aControls; + return NS_OK; +} + +// wrappers for ldap_add_ext +// +nsresult +nsLDAPOperation::AddExt(const char *base, + nsIArray *mods, + LDAPControl **serverctrls, + LDAPControl **clientctrls) +{ + if (!mMessageListener) { + NS_ERROR("nsLDAPOperation::AddExt(): mMessageListener not set"); + return NS_ERROR_NOT_INITIALIZED; + } + + LDAPMod **attrs = 0; + int retVal = LDAP_SUCCESS; + uint32_t modCount = 0; + nsresult rv = mods->GetLength(&modCount); + NS_ENSURE_SUCCESS(rv, rv); + + if (mods && modCount) { + attrs = static_cast(moz_xmalloc((modCount + 1) * + sizeof(LDAPMod *))); + if (!attrs) { + NS_ERROR("nsLDAPOperation::AddExt: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + + nsAutoCString type; + uint32_t index; + for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) { + attrs[index] = new LDAPMod(); + + if (!attrs[index]) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr modif(do_QueryElementAt(mods, index, &rv)); + if (NS_FAILED(rv)) + break; + +#ifdef NS_DEBUG + int32_t operation; + NS_ASSERTION(NS_SUCCEEDED(modif->GetOperation(&operation)) && + ((operation & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD), + "AddExt can only add."); +#endif + + attrs[index]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + + nsresult rv = modif->GetType(type); + if (NS_FAILED(rv)) + break; + + attrs[index]->mod_type = ToNewCString(type); + + rv = CopyValues(modif, &attrs[index]->mod_bvalues); + if (NS_FAILED(rv)) + break; + } + + if (NS_SUCCEEDED(rv)) { + attrs[modCount] = 0; + + retVal = ldap_add_ext(mConnectionHandle, base, attrs, + serverctrls, clientctrls, &mMsgID); + } + else + // reset the modCount so we correctly free the array. + modCount = index; + } + + for (uint32_t counter = 0; counter < modCount; ++counter) + delete attrs[counter]; + + free(attrs); + + return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal); +} + +/** + * wrapper for ldap_add_ext(): kicks off an async add request. + * + * @param aBaseDn Base DN to search + * @param aModCount Number of modifications + * @param aMods Array of modifications + * + * XXX doesn't currently handle LDAPControl params + * + * void addExt (in AUTF8String aBaseDn, in unsigned long aModCount, + * [array, size_is (aModCount)] in nsILDAPModification aMods); + */ +NS_IMETHODIMP +nsLDAPOperation::AddExt(const nsACString& aBaseDn, + nsIArray *aMods) +{ + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AddExt(): called with aBaseDn = '%s'", + PromiseFlatCString(aBaseDn).get())); + + nsresult rv = AddExt(PromiseFlatCString(aBaseDn).get(), aMods, 0, 0); + if (NS_FAILED(rv)) + return rv; + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + + if (NS_FAILED(rv)) { + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AddExt(): abandoned due to rv %x", + rv)); + } + return rv; +} + +// wrappers for ldap_delete_ext +// +nsresult +nsLDAPOperation::DeleteExt(const char *base, + LDAPControl **serverctrls, + LDAPControl **clientctrls) +{ + if (!mMessageListener) { + NS_ERROR("nsLDAPOperation::DeleteExt(): mMessageListener not set"); + return NS_ERROR_NOT_INITIALIZED; + } + + return TranslateLDAPErrorToNSError(ldap_delete_ext(mConnectionHandle, base, + serverctrls, clientctrls, + &mMsgID)); +} + +/** + * wrapper for ldap_delete_ext(): kicks off an async delete request. + * + * @param aBaseDn Base DN to delete + * + * XXX doesn't currently handle LDAPControl params + * + * void deleteExt(in AUTF8String aBaseDn); + */ +NS_IMETHODIMP +nsLDAPOperation::DeleteExt(const nsACString& aBaseDn) +{ + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::DeleteExt(): called with aBaseDn = '%s'", + PromiseFlatCString(aBaseDn).get())); + + nsresult rv = DeleteExt(PromiseFlatCString(aBaseDn).get(), 0, 0); + if (NS_FAILED(rv)) + return rv; + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + + if (NS_FAILED(rv)) { + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AddExt(): abandoned due to rv %x", + rv)); + } + return rv; +} + +// wrappers for ldap_modify_ext +// +nsresult +nsLDAPOperation::ModifyExt(const char *base, + nsIArray *mods, + LDAPControl **serverctrls, + LDAPControl **clientctrls) +{ + if (!mMessageListener) { + NS_ERROR("nsLDAPOperation::ModifyExt(): mMessageListener not set"); + return NS_ERROR_NOT_INITIALIZED; + } + + LDAPMod **attrs = 0; + int retVal = 0; + uint32_t modCount = 0; + nsresult rv = mods->GetLength(&modCount); + NS_ENSURE_SUCCESS(rv, rv); + if (modCount && mods) { + attrs = static_cast(moz_xmalloc((modCount + 1) * + sizeof(LDAPMod *))); + if (!attrs) { + NS_ERROR("nsLDAPOperation::ModifyExt: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + + nsAutoCString type; + uint32_t index; + for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) { + attrs[index] = new LDAPMod(); + if (!attrs[index]) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr modif(do_QueryElementAt(mods, index, &rv)); + if (NS_FAILED(rv)) + break; + + int32_t operation; + nsresult rv = modif->GetOperation(&operation); + if (NS_FAILED(rv)) + break; + + attrs[index]->mod_op = operation | LDAP_MOD_BVALUES; + + rv = modif->GetType(type); + if (NS_FAILED(rv)) + break; + + attrs[index]->mod_type = ToNewCString(type); + + rv = CopyValues(modif, &attrs[index]->mod_bvalues); + if (NS_FAILED(rv)) + break; + } + + if (NS_SUCCEEDED(rv)) { + attrs[modCount] = 0; + + retVal = ldap_modify_ext(mConnectionHandle, base, attrs, + serverctrls, clientctrls, &mMsgID); + } + else + // reset the modCount so we correctly free the array. + modCount = index; + + } + + for (uint32_t counter = 0; counter < modCount; ++counter) + delete attrs[counter]; + + free(attrs); + + return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal); +} + +/** + * wrapper for ldap_modify_ext(): kicks off an async modify request. + * + * @param aBaseDn Base DN to modify + * @param aModCount Number of modifications + * @param aMods Array of modifications + * + * XXX doesn't currently handle LDAPControl params + * + * void modifyExt (in AUTF8String aBaseDn, in unsigned long aModCount, + * [array, size_is (aModCount)] in nsILDAPModification aMods); + */ +NS_IMETHODIMP +nsLDAPOperation::ModifyExt(const nsACString& aBaseDn, + nsIArray *aMods) +{ + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::ModifyExt(): called with aBaseDn = '%s'", + PromiseFlatCString(aBaseDn).get())); + + nsresult rv = ModifyExt(PromiseFlatCString(aBaseDn).get(), + aMods, 0, 0); + if (NS_FAILED(rv)) + return rv; + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + + if (NS_FAILED(rv)) { + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AddExt(): abandoned due to rv %x", + rv)); + } + return rv; +} + +// wrappers for ldap_rename +// +nsresult +nsLDAPOperation::Rename(const char *base, + const char *newRDn, + const char *newParent, + bool deleteOldRDn, + LDAPControl **serverctrls, + LDAPControl **clientctrls) +{ + if (!mMessageListener) { + NS_ERROR("nsLDAPOperation::Rename(): mMessageListener not set"); + return NS_ERROR_NOT_INITIALIZED; + } + + return TranslateLDAPErrorToNSError(ldap_rename(mConnectionHandle, base, + newRDn, newParent, + deleteOldRDn, serverctrls, + clientctrls, &mMsgID)); +} + +/** + * wrapper for ldap_rename(): kicks off an async rename request. + * + * @param aBaseDn Base DN to rename + * @param aNewRDn New relative DN + * @param aNewParent DN of the new parent under which to move the + * + * XXX doesn't currently handle LDAPControl params + * + * void rename(in AUTF8String aBaseDn, in AUTF8String aNewRDn, + * in AUTF8String aNewParent, in boolean aDeleteOldRDn); + */ +NS_IMETHODIMP +nsLDAPOperation::Rename(const nsACString& aBaseDn, + const nsACString& aNewRDn, + const nsACString& aNewParent, + bool aDeleteOldRDn) +{ + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::Rename(): called with aBaseDn = '%s'", + PromiseFlatCString(aBaseDn).get())); + + nsresult rv = Rename(PromiseFlatCString(aBaseDn).get(), + PromiseFlatCString(aNewRDn).get(), + PromiseFlatCString(aNewParent).get(), + aDeleteOldRDn, 0, 0); + if (NS_FAILED(rv)) + return rv; + + // make sure the connection knows where to call back once the messages + // for this operation start coming in + rv = mConnection->AddPendingOperation(mMsgID, this); + + if (NS_FAILED(rv)) { + (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AddExt(): abandoned due to rv %x", + rv)); + } + return rv; +} + +// wrappers for ldap_search_ext +// + +/* static */ +nsresult +nsLDAPOperation::CopyValues(nsILDAPModification* aMod, berval*** aBValues) +{ + nsCOMPtr values; + nsresult rv = aMod->GetValues(getter_AddRefs(values)); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t valuesCount; + rv = values->GetLength(&valuesCount); + NS_ENSURE_SUCCESS(rv, rv); + + *aBValues = static_cast + (moz_xmalloc((valuesCount + 1) * + sizeof(berval *))); + if (!*aBValues) + return NS_ERROR_OUT_OF_MEMORY; + + uint32_t valueIndex; + for (valueIndex = 0; valueIndex < valuesCount; ++valueIndex) { + nsCOMPtr value(do_QueryElementAt(values, valueIndex, &rv)); + + berval* bval = new berval; + if (NS_FAILED(rv) || !bval) { + for (uint32_t counter = 0; + counter < valueIndex && counter < valuesCount; + ++counter) + delete (*aBValues)[valueIndex]; + + free(*aBValues); + delete bval; + return NS_ERROR_OUT_OF_MEMORY; + } + value->Get((uint32_t*)&bval->bv_len, + (uint8_t**)&bval->bv_val); + (*aBValues)[valueIndex] = bval; + } + + (*aBValues)[valuesCount] = 0; + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPOperation.h b/ldap/xpcom/src/nsLDAPOperation.h new file mode 100644 index 0000000000..7a74046d78 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPOperation.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _nsLDAPOperation_h_ +#define _nsLDAPOperation_h_ + +#include "ldap.h" +#include "nsCOMPtr.h" +#include "nsILDAPConnection.h" +#include "nsILDAPOperation.h" +#include "nsILDAPMessageListener.h" +#include "nsStringGlue.h" +#include "nsIMutableArray.h" +#include "nsLDAPConnection.h" + +// 97a479d0-9a44-47c6-a17a-87f9b00294bb +#define NS_LDAPOPERATION_CID \ +{ 0x97a479d0, 0x9a44, 0x47c6, \ + { 0xa1, 0x7a, 0x87, 0xf9, 0xb0, 0x02, 0x94, 0xbb}} + +class nsLDAPOperation : public nsILDAPOperation +{ + public: + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPOPERATION + + // constructor & destructor + // + nsLDAPOperation(); + + /** + * used to break cycles + */ + void Clear(); + + private: + virtual ~nsLDAPOperation(); + + /** + * wrapper for ldap_add_ext() + * + * XXX should move to idl, once LDAPControls have an IDL representation + */ + nsresult AddExt(const char *base, // base DN to add + nsIArray *mods, // Array of modifications + LDAPControl **serverctrls, + LDAPControl **clientctrls); + + /** + * wrapper for ldap_delete_ext() + * + * XXX should move to idl, once LDAPControls have an IDL representation + */ + nsresult DeleteExt(const char *base, // base DN to delete + LDAPControl **serverctrls, + LDAPControl **clientctrls); + + /** + * wrapper for ldap_modify_ext() + * + * XXX should move to idl, once LDAPControls have an IDL representation + */ + nsresult ModifyExt(const char *base, // base DN to modify + nsIArray *mods, // array of modifications + LDAPControl **serverctrls, + LDAPControl **clientctrls); + + /** + * wrapper for ldap_rename() + * + * XXX should move to idl, once LDAPControls have an IDL representation + */ + nsresult Rename(const char *base, // base DN to rename + const char *newRDn, // new RDN + const char *newParent, // DN of the new parent + bool deleteOldRDn, // remove old RDN in the entry? + LDAPControl **serverctrls, + LDAPControl **clientctrls); + + /** + * Helper function to copy the values of an nsILDAPModification into an + * array of berval's. + */ + static nsresult CopyValues(nsILDAPModification* aMod, berval*** aBValues); + + nsCOMPtr mMessageListener; // results go here + nsCOMPtr mClosure; // private parameter (anything caller desires) + RefPtr mConnection; // connection this op is on + + LDAP *mConnectionHandle; // cache connection handle + nsCString mSavePassword; + nsCString mMechanism; + nsCOMPtr mAuthModule; + int32_t mMsgID; // opaque handle to outbound message for this op + + nsCOMPtr mClientControls; + nsCOMPtr mServerControls; +}; + +#endif // _nsLDAPOperation_h diff --git a/ldap/xpcom/src/nsLDAPProtocolHandler.js b/ldap/xpcom/src/nsLDAPProtocolHandler.js new file mode 100644 index 0000000000..71fe082ff3 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPProtocolHandler.js @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const kNetworkProtocolCIDPrefix = "@mozilla.org/network/protocol;1?name="; +const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler; + +function makeProtocolHandler(aCID, aProtocol, aDefaultPort) { + return { + classID: Components.ID(aCID), + QueryInterface: XPCOMUtils.generateQI([nsIProtocolHandler]), + + scheme: aProtocol, + defaultPort: aDefaultPort, + protocolFlags: nsIProtocolHandler.URI_NORELATIVE | + nsIProtocolHandler.URI_DANGEROUS_TO_LOAD | + nsIProtocolHandler.ALLOWS_PROXY, + + newURI: function (aSpec, aOriginCharset, aBaseURI) { + var url = Components.classes["@mozilla.org/network/ldap-url;1"] + .createInstance(Components.interfaces.nsIURI); + + if (url instanceof Components.interfaces.nsILDAPURL) + url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD, + aDefaultPort, aSpec, aOriginCharset, aBaseURI); + + return url; + }, + + newChannel: function (aURI) { + return this.newChannel2(aURI, null); + }, + + newChannel2: function (aURI, aLoadInfo) { + if ("@mozilla.org/network/ldap-channel;1" in Components.classes) { + var channel = Components.classes["@mozilla.org/network/ldap-channel;1"] + .createInstance(Components.interfaces.nsIChannel); + channel.init(aURI); + return channel; + } + + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, + + allowPort: function (port, scheme) { + return port == aDefaultPort; + } + }; +} + +function nsLDAPProtocolHandler() {} + +nsLDAPProtocolHandler.prototype = makeProtocolHandler("{b3de9249-b0e5-4c12-8d91-c9a434fd80f5}", "ldap", 389); + +function nsLDAPSProtocolHandler() {} + +nsLDAPSProtocolHandler.prototype = makeProtocolHandler("{c85a5ef2-9c56-445f-b029-76889f2dd29b}", "ldaps", 636); + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsLDAPProtocolHandler, + nsLDAPSProtocolHandler]); diff --git a/ldap/xpcom/src/nsLDAPProtocolModule.cpp b/ldap/xpcom/src/nsLDAPProtocolModule.cpp new file mode 100644 index 0000000000..6f72e7c43c --- /dev/null +++ b/ldap/xpcom/src/nsLDAPProtocolModule.cpp @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIClassInfoImpl.h" +#include "mozilla/ModuleUtils.h" + +#include "nsLDAPInternal.h" +#include "nsLDAPURL.h" +#include "nsLDAPConnection.h" +#include "nsLDAPOperation.h" +#include "nsLDAPMessage.h" +#include "nsLDAPModification.h" +#include "nsLDAPServer.h" +#include "nsLDAPService.h" +#include "nsLDAPBERValue.h" +#include "nsLDAPBERElement.h" +#include "nsLDAPControl.h" +#ifdef MOZ_PREF_EXTENSIONS +#include "nsLDAPSyncQuery.h" +#endif +#include "ldappr.h" +#include "mozilla/Logging.h" + +// use the default constructor +// +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPConnection) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPOperation) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPMessage) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPModification, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPServer) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPURL) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPService, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERValue) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERElement) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPControl) +#ifdef MOZ_PREF_EXTENSIONS +NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPSyncQuery) +#endif + +NS_DEFINE_NAMED_CID(NS_LDAPCONNECTION_CID); +NS_DEFINE_NAMED_CID(NS_LDAPOPERATION_CID); +NS_DEFINE_NAMED_CID(NS_LDAPMESSAGE_CID); +NS_DEFINE_NAMED_CID(NS_LDAPMODIFICATION_CID); +NS_DEFINE_NAMED_CID(NS_LDAPSERVER_CID); +NS_DEFINE_NAMED_CID(NS_LDAPSERVICE_CID); +NS_DEFINE_NAMED_CID(NS_LDAPURL_CID); +NS_DEFINE_NAMED_CID(NS_LDAPBERVALUE_CID); +NS_DEFINE_NAMED_CID(NS_LDAPBERELEMENT_CID); +#ifdef MOZ_PREF_EXTENSIONS +NS_DEFINE_NAMED_CID(NS_LDAPSYNCQUERY_CID); +#endif +NS_DEFINE_NAMED_CID(NS_LDAPCONTROL_CID); + +// a table of the CIDs implemented by this module +// + +const mozilla::Module::CIDEntry kLDAPProtocolCIDs[] = { + { &kNS_LDAPCONNECTION_CID, false, NULL, nsLDAPConnectionConstructor}, + { &kNS_LDAPOPERATION_CID, false, NULL, nsLDAPOperationConstructor}, + { &kNS_LDAPMESSAGE_CID, false, NULL, nsLDAPMessageConstructor}, + { &kNS_LDAPMODIFICATION_CID, false, NULL, nsLDAPModificationConstructor}, + { &kNS_LDAPSERVER_CID, false, NULL, nsLDAPServerConstructor}, + { &kNS_LDAPSERVICE_CID, false, NULL, nsLDAPServiceConstructor}, + { &kNS_LDAPURL_CID, false, NULL, nsLDAPURLConstructor}, + { &kNS_LDAPBERVALUE_CID, false, NULL, nsLDAPBERValueConstructor}, + { &kNS_LDAPBERELEMENT_CID, false, NULL, nsLDAPBERElementConstructor}, +#ifdef MOZ_PREF_EXTENSIONS + { &kNS_LDAPSYNCQUERY_CID, false, NULL, nsLDAPSyncQueryConstructor}, +#endif + { &kNS_LDAPCONTROL_CID, false, NULL, nsLDAPControlConstructor}, + { NULL } +}; + + +const mozilla::Module::ContractIDEntry kLDAPProtocolContracts[] = { + { "@mozilla.org/network/ldap-connection;1", &kNS_LDAPCONNECTION_CID}, + { "@mozilla.org/network/ldap-operation;1", &kNS_LDAPOPERATION_CID}, + { "@mozilla.org/network/ldap-message;1", &kNS_LDAPMESSAGE_CID}, + { "@mozilla.org/network/ldap-modification;1", &kNS_LDAPMODIFICATION_CID}, + { "@mozilla.org/network/ldap-server;1", &kNS_LDAPSERVER_CID}, + { "@mozilla.org/network/ldap-service;1", &kNS_LDAPSERVICE_CID}, + { "@mozilla.org/network/ldap-url;1", &kNS_LDAPURL_CID}, + { "@mozilla.org/network/ldap-ber-value;1", &kNS_LDAPBERVALUE_CID}, + { "@mozilla.org/network/ldap-ber-element;1", &kNS_LDAPBERELEMENT_CID}, +#ifdef MOZ_PREF_EXTENSIONS + { "@mozilla.org/ldapsyncquery;1", &kNS_LDAPSYNCQUERY_CID}, +#endif + { "@mozilla.org/network/ldap-control;1", &kNS_LDAPCONTROL_CID}, + { NULL } +}; + +static nsresult +nsLDAPInitialize() +{ +#ifdef PR_LOGGING + gLDAPLogModule = PR_NewLogModule("ldap"); + if (!gLDAPLogModule) { + PR_fprintf(PR_STDERR, + "nsLDAP_Initialize(): PR_NewLogModule() failed\n"); + return NS_ERROR_NOT_AVAILABLE; + } +#endif + + // use NSPR under the hood for all networking + // + int rv = prldap_install_routines( NULL, 1 /* shared */ ); + + if (rv != LDAP_SUCCESS) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, + ("nsLDAPInitialize(): pr_ldap_install_routines() failed: %s\n", + ldap_err2string(rv))); + return NS_ERROR_FAILURE; + } + + // Never block for more than 10000 milliseconds (ie 10 seconds) doing any + // sort of I/O operation. + // + rv = prldap_set_session_option(0, 0, PRLDAP_OPT_IO_MAX_TIMEOUT, + 10000); + if (rv != LDAP_SUCCESS) { + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, + ("nsLDAPInitialize(): error setting PRLDAP_OPT_IO_MAX_TIMEOUT:" + " %s\n", ldap_err2string(rv))); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +static const mozilla::Module kLDAPProtocolModule = { + mozilla::Module::kVersion, + kLDAPProtocolCIDs, + kLDAPProtocolContracts, + NULL, + NULL, + nsLDAPInitialize, + NULL +}; + +NSMODULE_DEFN(nsLDAPProtocolModule) = &kLDAPProtocolModule; + +#ifdef PR_LOGGING +PRLogModuleInfo *gLDAPLogModule = 0; +#endif diff --git a/ldap/xpcom/src/nsLDAPSecurityGlue.cpp b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp new file mode 100644 index 0000000000..c39723a72a --- /dev/null +++ b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp @@ -0,0 +1,342 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Only build this code if PSM is being built also +// +#include "nsLDAPInternal.h" +#include "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsIInterfaceRequestor.h" +#include "nsNetCID.h" +#include "nsISocketProvider.h" +#include "nsISSLSocketControl.h" +#include "nsString.h" +#include "nsMemory.h" +#include "plstr.h" +#include "ldap.h" +#include "ldappr.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" + +// LDAP per-session data structure. +// +typedef struct { + char *hostname; + LDAP_X_EXTIOF_CLOSE_CALLBACK *realClose; + LDAP_X_EXTIOF_CONNECT_CALLBACK *realConnect; + LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *realDisposeHandle; +} nsLDAPSSLSessionClosure; + +// LDAP per-socket data structure. +// +typedef struct { + nsLDAPSSLSessionClosure *sessionClosure; /* session info */ +} nsLDAPSSLSocketClosure; + +// free the per-socket data structure as necessary +// +static void +nsLDAPSSLFreeSocketClosure(nsLDAPSSLSocketClosure **aClosure) +{ + if (aClosure && *aClosure) { + free(*aClosure); + *aClosure = nullptr; + } +} + +// Replacement close() function, which cleans up local stuff associated +// with this socket, and then calls the real close function. +// +extern "C" int LDAP_CALLBACK +nsLDAPSSLClose(int s, struct lextiof_socket_private *socketarg) +{ + PRLDAPSocketInfo socketInfo; + nsLDAPSSLSocketClosure *socketClosure; + nsLDAPSSLSessionClosure *sessionClosure; + + // get the socketInfo associated with this socket + // + memset(&socketInfo, 0, sizeof(socketInfo)); + socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE; + if (prldap_get_socket_info(s, socketarg, &socketInfo) != LDAP_SUCCESS) { + NS_ERROR("nsLDAPSSLClose(): prldap_get_socket_info() failed\n"); + return -1; + } + + // save off the session closure data in an automatic, since we're going to + // need to call through it + // + socketClosure = reinterpret_cast + (socketInfo.soinfo_appdata); + sessionClosure = socketClosure->sessionClosure; + + // free the socket closure data + // + nsLDAPSSLFreeSocketClosure( + reinterpret_cast + (&socketInfo.soinfo_appdata)); + + // call the real close function + // + return (*(sessionClosure->realClose))(s, socketarg); +} + +// Replacement connection function. Calls the real connect function, +// +extern "C" int LDAP_CALLBACK +nsLDAPSSLConnect(const char *hostlist, int defport, int timeout, + unsigned long options, + struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ) +{ + PRLDAPSocketInfo socketInfo; + PRLDAPSessionInfo sessionInfo; + nsLDAPSSLSocketClosure *socketClosure = nullptr; + nsLDAPSSLSessionClosure *sessionClosure; + int intfd = -1; + nsCOMPtr securityInfo; + nsCOMPtr tlsSocketProvider; + nsCOMPtr sslSocketControl; + nsresult rv; + + // Ensure secure option is set. Also, clear secure bit in options + // the we pass to the standard connect() function (since it doesn't know + // how to handle the secure option). + // + NS_ASSERTION(options & LDAP_X_EXTIOF_OPT_SECURE, + "nsLDAPSSLConnect(): called for non-secure connection"); + options &= ~LDAP_X_EXTIOF_OPT_SECURE; + + // Retrieve session info. so we can store a pointer to our session info. + // in our socket info. later. + // + memset(&sessionInfo, 0, sizeof(sessionInfo)); + sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE; + if (prldap_get_session_info(nullptr, sessionarg, &sessionInfo) + != LDAP_SUCCESS) { + NS_ERROR("nsLDAPSSLConnect(): unable to get session info"); + return -1; + } + sessionClosure = reinterpret_cast + (sessionInfo.seinfo_appdata); + + // Call the real connect() callback to make the TCP connection. If it + // succeeds, *socketargp is set. + // + intfd = (*(sessionClosure->realConnect))(hostlist, defport, timeout, + options, sessionarg, socketargp); + if ( intfd < 0 ) { + PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, + ("nsLDAPSSLConnect(): standard connect() function returned %d", + intfd)); + return intfd; + } + + // Retrieve socket info from the newly created socket so that we + // have the PRFileDesc onto which we will be layering SSL. + // + memset(&socketInfo, 0, sizeof(socketInfo)); + socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE; + if (prldap_get_socket_info(intfd, *socketargp, &socketInfo) + != LDAP_SUCCESS) { + NS_ERROR("nsLDAPSSLConnect(): unable to get socket info"); + goto close_socket_and_exit_with_error; + } + + // Allocate a structure to hold our socket-specific data. + // + socketClosure = static_cast(moz_xmalloc( + sizeof(nsLDAPSSLSocketClosure))); + if (!socketClosure) { + NS_WARNING("nsLDAPSSLConnect(): unable to allocate socket closure"); + goto close_socket_and_exit_with_error; + } + memset(socketClosure, 0, sizeof(nsLDAPSSLSocketClosure)); + socketClosure->sessionClosure = sessionClosure; + + // Add the NSPR layer for SSL provided by PSM to this socket. + // + tlsSocketProvider = do_GetService(NS_STARTTLSSOCKETPROVIDER_CONTRACTID, + &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSSLConnect(): unable to get socket provider service"); + goto close_socket_and_exit_with_error; + } + // XXXdmose: Note that hostlist can be a list of hosts (in the + // current XPCOM SDK code, it will always be a list of IP + // addresses). Because of this, we need to use + // sessionClosure->hostname which was passed in separately to tell + // AddToSocket what to match the name in the certificate against. + // What exactly happen will happen when this is used with some IP + // address in the list other than the first one is not entirely + // clear, and I suspect it may depend on the format of the name in + // the certificate. Need to investigate. + // + rv = tlsSocketProvider->AddToSocket(PR_AF_INET, + sessionClosure->hostname, + defport, + nullptr, + mozilla::NeckoOriginAttributes(), + 0, + socketInfo.soinfo_prfd, + getter_AddRefs(securityInfo)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket"); + goto close_socket_and_exit_with_error; + } + + // If possible we want to avoid using SSLv2, as this can confuse + // some directory servers (notably the netscape 4.1 ds). The only + // way that PSM provides for us to do this is to use a socket that can + // be used for the STARTTLS protocol, because the STARTTLS protocol disallows + // the use of SSLv2. + // (Thanks to Brian Ryner for helping figure this out). + // + sslSocketControl = do_QueryInterface(securityInfo, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("nsLDAPSSLConnect(): unable to QI to nsISSLSocketControl"); + } else { + rv = sslSocketControl->StartTLS(); + if (NS_FAILED(rv)) { + NS_WARNING("nsLDAPSSLConnect(): StartTLS failed"); + } + } + + // Attach our closure to the socketInfo. + // + socketInfo.soinfo_appdata = reinterpret_cast + (socketClosure); + if (prldap_set_socket_info(intfd, *socketargp, &socketInfo) + != LDAP_SUCCESS ) { + NS_ERROR("nsLDAPSSLConnect(): unable to set socket info"); + } + return intfd; // success + +close_socket_and_exit_with_error: + if (socketInfo.soinfo_prfd) { + PR_Close(socketInfo.soinfo_prfd); + } + if (socketClosure) { + nsLDAPSSLFreeSocketClosure(&socketClosure); + } + if ( intfd >= 0 && *socketargp ) { + (*(sessionClosure->realClose))(intfd, *socketargp); + } + return -1; + +} + +// Free data associated with this session (LDAP *) as necessary. +// +static void +nsLDAPSSLFreeSessionClosure(nsLDAPSSLSessionClosure **aSessionClosure) +{ + if (aSessionClosure && *aSessionClosure) { + + // free the hostname + // + if ( (*aSessionClosure)->hostname ) { + PL_strfree((*aSessionClosure)->hostname); + (*aSessionClosure)->hostname = nullptr; + } + + // free the structure itself + // + free(*aSessionClosure); + *aSessionClosure = nullptr; + } +} + +// Replacement session handle disposal code. First cleans up our local +// stuff, then calls the original session handle disposal function. +// +extern "C" void LDAP_CALLBACK +nsLDAPSSLDisposeHandle(LDAP *ld, struct lextiof_session_private *sessionarg) +{ + PRLDAPSessionInfo sessionInfo; + nsLDAPSSLSessionClosure *sessionClosure; + LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn; + + memset(&sessionInfo, 0, sizeof(sessionInfo)); + sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE; + if (prldap_get_session_info(ld, nullptr, &sessionInfo) == LDAP_SUCCESS) { + sessionClosure = reinterpret_cast + (sessionInfo.seinfo_appdata); + disposehdl_fn = sessionClosure->realDisposeHandle; + nsLDAPSSLFreeSessionClosure(&sessionClosure); + (*disposehdl_fn)(ld, sessionarg); + } +} + +// Installs appropriate routines and data for making this connection +// handle SSL. The aHostName is ultimately passed to PSM and is used to +// validate certificates. +// +nsresult +nsLDAPInstallSSL( LDAP *ld, const char *aHostName) +{ + struct ldap_x_ext_io_fns iofns; + nsLDAPSSLSessionClosure *sessionClosure; + PRLDAPSessionInfo sessionInfo; + + // Allocate our own session information. + // + sessionClosure = static_cast(moz_xmalloc( + sizeof(nsLDAPSSLSessionClosure))); + if (!sessionClosure) { + return NS_ERROR_OUT_OF_MEMORY; + } + memset(sessionClosure, 0, sizeof(nsLDAPSSLSessionClosure)); + + // Override a few functions, saving a pointer to the original function + // in each case so we can call it from our SSL savvy functions. + // + memset(&iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if (ldap_get_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS, + static_cast(&iofns)) != LDAP_SUCCESS) { + NS_ERROR("nsLDAPInstallSSL(): unexpected error getting" + " LDAP_X_OPT_EXTIO_FN_PTRS"); + nsLDAPSSLFreeSessionClosure(&sessionClosure); + return NS_ERROR_UNEXPECTED; + } + + // Make a copy of the hostname to pass to AddToSocket later + // + sessionClosure->hostname = PL_strdup(aHostName); + if (!sessionClosure->hostname) { + NS_ERROR("nsLDAPInstallSSL(): PL_strdup failed\n"); + nsLDAPSSLFreeSessionClosure(&sessionClosure); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Override functions + // + sessionClosure->realClose = iofns.lextiof_close; + iofns.lextiof_close = nsLDAPSSLClose; + sessionClosure->realConnect = iofns.lextiof_connect; + iofns.lextiof_connect = nsLDAPSSLConnect; + sessionClosure->realDisposeHandle = iofns.lextiof_disposehandle; + iofns.lextiof_disposehandle = nsLDAPSSLDisposeHandle; + + if (ldap_set_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS, + static_cast(&iofns)) != LDAP_SUCCESS) { + NS_ERROR("nsLDAPInstallSSL(): error setting LDAP_X_OPT_EXTIO_FN_PTRS"); + nsLDAPSSLFreeSessionClosure(&sessionClosure); + return NS_ERROR_FAILURE; + } + + // Store session info. for later retrieval. + // + sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE; + sessionInfo.seinfo_appdata = reinterpret_cast + (sessionClosure); + if (prldap_set_session_info(ld, nullptr, &sessionInfo) != LDAP_SUCCESS) { + NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info"); + free(sessionClosure); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPServer.cpp b/ldap/xpcom/src/nsLDAPServer.cpp new file mode 100644 index 0000000000..34504c5c7e --- /dev/null +++ b/ldap/xpcom/src/nsLDAPServer.cpp @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPServer.h" + +NS_IMPL_ISUPPORTS(nsLDAPServer, nsILDAPServer) + +nsLDAPServer::nsLDAPServer() + : mSizeLimit(0), + mProtocolVersion(nsILDAPConnection::VERSION3) +{ +} + +nsLDAPServer::~nsLDAPServer() +{ +} + +// attribute wstring key; +NS_IMETHODIMP nsLDAPServer::GetKey(char16_t **_retval) +{ + if (!_retval) { + NS_ERROR("nsLDAPServer::GetKey: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + *_retval = ToNewUnicode(mKey); + if (!*_retval) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetKey(const char16_t *aKey) +{ + mKey = aKey; + return NS_OK; +} + +// attribute AUTF8String username; +NS_IMETHODIMP nsLDAPServer::GetUsername(nsACString& _retval) +{ + _retval.Assign(mUsername); + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetUsername(const nsACString& aUsername) +{ + mUsername.Assign(aUsername); + return NS_OK; +} + +// attribute AUTF8String password; +NS_IMETHODIMP nsLDAPServer::GetPassword(nsACString& _retval) +{ + _retval.Assign(mPassword); + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetPassword(const nsACString& aPassword) +{ + mPassword.Assign(aPassword); + return NS_OK; +} + +// attribute AUTF8String binddn; +NS_IMETHODIMP nsLDAPServer::GetBinddn(nsACString& _retval) +{ + _retval.Assign(mBindDN); + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetBinddn(const nsACString& aBindDN) +{ + mBindDN.Assign(aBindDN); + return NS_OK; +} + +// attribute unsigned long sizelimit; +NS_IMETHODIMP nsLDAPServer::GetSizelimit(uint32_t *_retval) +{ + if (!_retval) { + NS_ERROR("nsLDAPServer::GetSizelimit: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + *_retval = mSizeLimit; + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetSizelimit(uint32_t aSizeLimit) +{ + mSizeLimit = aSizeLimit; + return NS_OK; +} + +// attribute nsILDAPURL url; +NS_IMETHODIMP nsLDAPServer::GetUrl(nsILDAPURL **_retval) +{ + if (!_retval) { + NS_ERROR("nsLDAPServer::GetUrl: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + NS_IF_ADDREF(*_retval = mURL); + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetUrl(nsILDAPURL *aURL) +{ + mURL = aURL; + return NS_OK; +} + +// attribute long protocolVersion +NS_IMETHODIMP nsLDAPServer::GetProtocolVersion(uint32_t *_retval) +{ + if (!_retval) { + NS_ERROR("nsLDAPServer::GetProtocolVersion: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + *_retval = mProtocolVersion; + return NS_OK; +} +NS_IMETHODIMP nsLDAPServer::SetProtocolVersion(uint32_t aVersion) +{ + if (aVersion != nsILDAPConnection::VERSION2 && + aVersion != nsILDAPConnection::VERSION3) { + NS_ERROR("nsLDAPServer::SetProtocolVersion: invalid version"); + return NS_ERROR_INVALID_ARG; + } + + mProtocolVersion = aVersion; + return NS_OK; +} diff --git a/ldap/xpcom/src/nsLDAPServer.h b/ldap/xpcom/src/nsLDAPServer.h new file mode 100644 index 0000000000..e8ec5da35c --- /dev/null +++ b/ldap/xpcom/src/nsLDAPServer.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsStringGlue.h" +#include "nsILDAPServer.h" +#include "nsILDAPURL.h" + +// 8bbbaa54-f316-4271-87c3-d52b5b1c1f5b +#define NS_LDAPSERVER_CID \ +{ 0x8bbbaa54, 0xf316, 0x4271, \ + { 0x87, 0xc3, 0xd5, 0x2b, 0x5b, 0x1c, 0x1f, 0x5b}} + +class nsLDAPServer : public nsILDAPServer +{ + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPSERVER + + // Constructor & destructor + // + nsLDAPServer(); + + protected: + virtual ~nsLDAPServer(); + + nsString mKey; // Unique identifier for this server object + nsCString mUsername; // Username / UID + nsCString mPassword; // Password to bind with + nsCString mBindDN; // DN associated with the UID above + uint32_t mSizeLimit; // Limit the LDAP search to this # of entries + uint32_t mProtocolVersion; // What version of LDAP to use? + // This "links" to a LDAP URL object, which holds further information + // related to the LDAP server. Like Host, port, base-DN and scope. + nsCOMPtr mURL; +}; diff --git a/ldap/xpcom/src/nsLDAPService.cpp b/ldap/xpcom/src/nsLDAPService.cpp new file mode 100644 index 0000000000..9445a5f33e --- /dev/null +++ b/ldap/xpcom/src/nsLDAPService.cpp @@ -0,0 +1,985 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPInternal.h" +#include "nsLDAPService.h" +#include "nsLDAPConnection.h" +#include "nsLDAPOperation.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "nsILDAPURL.h" +#include "nsMemory.h" +#include "nsILDAPErrors.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/Logging.h" + +using namespace mozilla; + +// Constants for CIDs used here. +// +static NS_DEFINE_CID(kLDAPConnectionCID, NS_LDAPCONNECTION_CID); +static NS_DEFINE_CID(kLDAPOperationCID, NS_LDAPOPERATION_CID); + +// First we provide all the methods for the "local" class +// nsLDAPServiceEntry. +// + +// constructor +// +nsLDAPServiceEntry::nsLDAPServiceEntry() + : mLeases(0), + mTimestamp(0), + mDelete(false), + mRebinding(false) + +{ +} + +// Init function +// +bool nsLDAPServiceEntry::Init() +{ + return true; +} + +// Set/Get the timestamp when this server was last used. We might have +// to use an "interval" here instead, see Bug #76887. +// +PRTime nsLDAPServiceEntry::GetTimestamp() +{ + return mTimestamp; +} +void nsLDAPServiceEntry::SetTimestamp() +{ + mTimestamp = PR_Now(); +} + + +// Increment, decrement and Get the leases. This code might go away +// with bug #75954. +// +void nsLDAPServiceEntry::IncrementLeases() +{ + mLeases++; +} +bool nsLDAPServiceEntry::DecrementLeases() +{ + if (!mLeases) { + return false; + } + mLeases--; + + return true; +} +uint32_t nsLDAPServiceEntry::GetLeases() +{ + return mLeases; +} + +// Get/Set the nsLDAPServer object for this entry. +// +already_AddRefed nsLDAPServiceEntry::GetServer() +{ + nsCOMPtr server = mServer; + return server.forget(); +} +bool nsLDAPServiceEntry::SetServer(nsILDAPServer *aServer) +{ + if (!aServer) { + return false; + } + mServer = aServer; + + return true; +} + +// Get/Set/Clear the nsLDAPConnection object for this entry. +// +already_AddRefed nsLDAPServiceEntry::GetConnection() +{ + nsCOMPtr conn = mConnection; + return conn.forget(); +} +void nsLDAPServiceEntry::SetConnection(nsILDAPConnection *aConnection) +{ + mConnection = aConnection; +} + +// Get/Set the nsLDAPMessage object for this entry (it's a "cache"). +// +already_AddRefed nsLDAPServiceEntry::GetMessage() +{ + nsCOMPtr message = mMessage; + return message.forget(); +} +void nsLDAPServiceEntry::SetMessage(nsILDAPMessage *aMessage) +{ + mMessage = aMessage; +} + +// Push/Pop pending listeners/callback for this server entry. This is +// implemented as a "stack" on top of the nsCOMArray, since we can +// potentially have more than one listener waiting for the connection +// to be available for consumption. +// +already_AddRefed nsLDAPServiceEntry::PopListener() +{ + if (mListeners.IsEmpty()) { + return 0; + } + + nsCOMPtr listener = mListeners[0]; + mListeners.RemoveObjectAt(0); + return listener.forget(); +} +bool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener) +{ + return mListeners.AppendObject(listener); +} + +// Mark this server to currently be rebinding. This is to avoid a +// race condition where multiple consumers could potentially request +// to reconnect the connection. +// +bool nsLDAPServiceEntry::IsRebinding() +{ + return mRebinding; +} +void nsLDAPServiceEntry::SetRebinding(bool aState) +{ + mRebinding = aState; +} + +// Mark a service entry for deletion, this is "dead" code right now, +// see bug #75966. +// +bool nsLDAPServiceEntry::DeleteEntry() +{ + mDelete = true; + + return true; +} +// This is the end of the nsLDAPServiceEntry class + + +// Here begins the implementation for nsLDAPService +// +NS_IMPL_ISUPPORTS(nsLDAPService, + nsILDAPService, + nsILDAPMessageListener) + + +// constructor +// +nsLDAPService::nsLDAPService() + : mLock("nsLDAPService.mLock") +{ +} + +// destructor +// +nsLDAPService::~nsLDAPService() +{ +} + +// Initializer +// +nsresult nsLDAPService::Init() +{ + return NS_OK; +} + +// void addServer (in nsILDAPServer aServer); +NS_IMETHODIMP nsLDAPService::AddServer(nsILDAPServer *aServer) +{ + nsLDAPServiceEntry *entry; + nsString key; + nsresult rv; + + if (!aServer) { + NS_ERROR("nsLDAPService::AddServer: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + // Set up the hash key for the server entry + // + rv = aServer->GetKey(getter_Copies(key)); + if (NS_FAILED(rv)) { + // Only pass along errors we are aware of + if ((rv == NS_ERROR_OUT_OF_MEMORY) || (rv == NS_ERROR_NULL_POINTER)) + return rv; + else + return NS_ERROR_FAILURE; + } + + // Create the new service server entry, and add it into the hash table + // + entry = new nsLDAPServiceEntry; + if (!entry) { + NS_ERROR("nsLDAPService::AddServer: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + if (!entry->Init()) { + delete entry; + NS_ERROR("nsLDAPService::AddServer: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!entry->SetServer(aServer)) { + delete entry; + return NS_ERROR_FAILURE; + } + + // We increment the refcount here for the server entry, when + // we purge a server completely from the service (TBD), we + // need to decrement the counter as well. + // + { + MutexAutoLock lock(mLock); + + if (mServers.Get(key)) { + // Collision detected, lets just throw away this service entry + // and keep the old one. + // + delete entry; + return NS_ERROR_FAILURE; + } + mServers.Put(key, entry); + } + NS_ADDREF(aServer); + + return NS_OK; +} + +// void deleteServer (in wstring aKey); +NS_IMETHODIMP nsLDAPService::DeleteServer(const char16_t *aKey) +{ + nsLDAPServiceEntry *entry; + MutexAutoLock lock(mLock); + + // We should probably rename the key for this entry now that it's + // "deleted", so that we can add in a new one with the same ID. + // This is bug #77669. + // + mServers.Get(nsDependentString(aKey), &entry); + if (entry) { + if (entry->GetLeases() > 0) { + return NS_ERROR_FAILURE; + } + entry->DeleteEntry(); + } else { + // There is no Server entry for this key + // + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +// nsILDAPServer getServer (in wstring aKey); +NS_IMETHODIMP nsLDAPService::GetServer(const char16_t *aKey, + nsILDAPServer **_retval) +{ + nsLDAPServiceEntry *entry; + MutexAutoLock lock(mLock); + + if (!_retval) { + NS_ERROR("nsLDAPService::GetServer: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + if (!mServers.Get(nsDependentString(aKey), &entry)) { + *_retval = 0; + return NS_ERROR_FAILURE; + } + if (!(*_retval = entry->GetServer().take())) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +//void requestConnection (in wstring aKey, +// in nsILDAPMessageListener aMessageListener); +NS_IMETHODIMP nsLDAPService::RequestConnection(const char16_t *aKey, + nsILDAPMessageListener *aListener) +{ + nsLDAPServiceEntry *entry; + nsCOMPtr conn; + nsCOMPtr message; + nsresult rv; + + if (!aListener) { + NS_ERROR("nsLDAPService::RequestConection: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + // Try to find a possibly cached connection and LDAP message. + // + { + MutexAutoLock lock(mLock); + + if (!mServers.Get(nsDependentString(aKey), &entry)) { + return NS_ERROR_FAILURE; + } + entry->SetTimestamp(); + + conn = entry->GetConnection(); + message = entry->GetMessage(); + } + + if (conn) { + if (message) { + // We already have the connection, and the message, ready to + // be used. This might be confusing, since we actually call + // the listener before this function returns, see bug #75899. + // + aListener->OnLDAPMessage(message); + return NS_OK; + } + } else { + rv = EstablishConnection(entry, aListener); + if (NS_FAILED(rv)) { + return rv; + } + + } + + // We got a new connection, now push the listeners on our stack, + // until we get the LDAP message back. + // + { + MutexAutoLock lock(mLock); + + if (!mServers.Get(nsDependentString(aKey), &entry) || + !entry->PushListener(static_cast + (aListener))) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +// nsILDAPConnection getConnection (in wstring aKey); +NS_IMETHODIMP nsLDAPService::GetConnection(const char16_t *aKey, + nsILDAPConnection **_retval) +{ + nsLDAPServiceEntry *entry; + MutexAutoLock lock(mLock); + + if (!_retval) { + NS_ERROR("nsLDAPService::GetConnection: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + if (!mServers.Get(nsDependentString(aKey), &entry)) { + *_retval = 0; + return NS_ERROR_FAILURE; + } + entry->SetTimestamp(); + entry->IncrementLeases(); + if (!(*_retval = entry->GetConnection().take())){ + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +// void releaseConnection (in wstring aKey); +NS_IMETHODIMP nsLDAPService::ReleaseConnection(const char16_t *aKey) +{ + nsLDAPServiceEntry *entry; + MutexAutoLock lock(mLock); + + if (!mServers.Get(nsDependentString(aKey), &entry)) { + return NS_ERROR_FAILURE; + } + + if (entry->GetLeases() > 0) { + entry->SetTimestamp(); + entry->DecrementLeases(); + } else { + // Releasing a non-leased connection is currently a No-Op. + // + } + + return NS_OK; +} + +// void reconnectConnection (in wstring aKey, +// in nsILDAPMessageListener aMessageListener); +NS_IMETHODIMP nsLDAPService::ReconnectConnection(const char16_t *aKey, + nsILDAPMessageListener *aListener) +{ + nsLDAPServiceEntry *entry; + nsresult rv; + + if (!aListener) { + NS_ERROR("nsLDAPService::ReconnectConnection: null pointer "); + return NS_ERROR_NULL_POINTER; + } + + { + MutexAutoLock lock(mLock); + + if (!mServers.Get(nsDependentString(aKey), &entry)) { + return NS_ERROR_FAILURE; + } + entry->SetTimestamp(); + + if (entry->IsRebinding()) { + if (!entry->PushListener(aListener)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + // Clear the old connection and message, which should get garbaged + // collected now. We mark this as being "rebinding" now, and it + // we be marked as finished either if there's an error condition, + // or if the OnLDAPMessage() method gets called (i.e. bind() done). + // + entry->SetMessage(0); + entry->SetConnection(0); + + // Get a new connection + // + entry->SetRebinding(true); + } + + rv = EstablishConnection(entry, aListener); + if (NS_FAILED(rv)) { + return rv; + } + + { + MutexAutoLock lock(mLock); + + if (!entry->PushListener(static_cast + (aListener))) { + entry->SetRebinding(false); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +/** + * Messages received are passed back via this function. + * + * @arg aMessage The message that was returned, 0 if none was. + * + * void OnLDAPMessage (in nsILDAPMessage aMessage) + */ +NS_IMETHODIMP +nsLDAPService::OnLDAPMessage(nsILDAPMessage *aMessage) +{ + nsCOMPtr operation; + nsCOMPtr connection; + int32_t messageType; + + // XXXleif: NULL messages are supposedly allowed, but the semantics + // are not definted (yet?). This is something to look out for... + // + + + // figure out what sort of message was returned + // + nsresult rv = aMessage->GetType(&messageType); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in " + "nsLDAPMessage::GetType()"); + return NS_ERROR_UNEXPECTED; + } + + switch (messageType) { + case LDAP_RES_BIND: + // a bind has completed + // + rv = aMessage->GetOperation(getter_AddRefs(operation)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in " + "nsLDAPMessage::GetOperation()"); + return NS_ERROR_UNEXPECTED; + } + + rv = operation->GetConnection(getter_AddRefs(connection)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in " + "nsLDAPOperation::GetConnection()"); + return NS_ERROR_UNEXPECTED; + } + + // Now we have the connection, lets find the corresponding + // server entry in the Service. + // + { + nsCOMPtr listener; + nsCOMPtr message; + nsLDAPServiceEntry *entry; + MutexAutoLock lock(mLock); + + if (!mConnections.Get(connection, &entry)) { + return NS_ERROR_FAILURE; + } + + message = entry->GetMessage(); + if (message) { + // We already have a message, lets keep that one. + // + return NS_ERROR_FAILURE; + } + + entry->SetRebinding(false); + entry->SetMessage(aMessage); + + // Now process all the pending callbacks/listeners. We + // have to make sure to unlock before calling a listener, + // since it's likely to call back into us again. + // + while ((listener = entry->PopListener())) { + MutexAutoUnlock unlock(mLock); + listener->OnLDAPMessage(aMessage); + } + } + break; + + default: + NS_WARNING("nsLDAPService::OnLDAPMessage(): unexpected LDAP message " + "received"); + + // get the console service so we can log a message + // + nsCOMPtr consoleSvc = + do_GetService("@mozilla.org/consoleservice;1", &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPChannel::OnLDAPMessage() couldn't get console " + "service"); + break; + } + + // log the message + // + rv = consoleSvc->LogStringMessage( + NS_LITERAL_STRING("LDAP: WARNING: nsLDAPService::OnLDAPMessage(): Unexpected LDAP message received").get()); + NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPService::OnLDAPMessage(): " + "consoleSvc->LogStringMessage() failed"); + break; + } + + return NS_OK; +} + +// void onLDAPInit (in nsILDAPConnection aConn, in nsresult aStatus); */ +// +NS_IMETHODIMP +nsLDAPService::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// Helper function to establish an LDAP connection properly. +// +nsresult +nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry, + nsILDAPMessageListener *aListener) +{ + nsCOMPtr operation; + nsCOMPtr server; + nsCOMPtr url; + nsCOMPtr conn, conn2; + nsCOMPtr message; + nsAutoCString binddn; + nsAutoCString password; + uint32_t protocolVersion; + nsresult rv; + + server = aEntry->GetServer(); + if (!server) { + return NS_ERROR_FAILURE; + } + + // Get username, password, and protocol version from the server entry. + // + rv = server->GetBinddn(binddn); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + rv = server->GetPassword(password); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + rv = server->GetProtocolVersion(&protocolVersion); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + // Get the host and port out of the URL, which is in the server entry. + // + rv = server->GetUrl(getter_AddRefs(url)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + // Create a new connection for this server. + // + conn = do_CreateInstance(kLDAPConnectionCID, &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPService::EstablishConnection(): could not create " + "@mozilla.org/network/ldap-connection;1"); + return NS_ERROR_FAILURE; + } + + rv = conn->Init(url, binddn, this, nullptr, protocolVersion); + if (NS_FAILED(rv)) { + switch (rv) { + // Only pass along errors we are aware of + // + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_NOT_AVAILABLE: + case NS_ERROR_FAILURE: + return rv; + + case NS_ERROR_ILLEGAL_VALUE: + default: + return NS_ERROR_UNEXPECTED; + } + } + + // Try to detect a collision, i.e. someone established a connection + // just before we did. If so, we drop ours. This code is somewhat + // similar to what happens in RequestConnection(), i.e. we try to + // call the listener directly if possible, and if not, push it on + // the stack of pending requests. + // + { + MutexAutoLock lock(mLock); + + conn2 = aEntry->GetConnection(); + message = aEntry->GetMessage(); + } + + if (conn2) { + // Drop the new connection, we can't use it. + // + conn = nullptr; + if (message) { + aListener->OnLDAPMessage(message); + return NS_OK; + } + + { + MutexAutoLock lock(mLock); + + if (!aEntry->PushListener(static_cast + (aListener))) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; + } + + // We made the connection, lets store it to the server entry, + // and also update the reverse lookup tables (for finding the + // server entry related to a particular connection). + // + { + MutexAutoLock lock(mLock); + + aEntry->SetConnection(conn); + mConnections.Put(conn, aEntry); + } + + // Setup the bind() operation. + // + operation = do_CreateInstance(kLDAPOperationCID, &rv); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + rv = operation->Init(conn, this, nullptr); + if (NS_FAILED(rv)) { + return NS_ERROR_UNEXPECTED; // this should never happen + } + + // Start a bind operation + // + // Here we need to support the password, see bug #75990 + // + rv = operation->SimpleBind(password); + if (NS_FAILED(rv)) { + // Only pass along errors we are aware of + if ((rv == NS_ERROR_LDAP_ENCODING_ERROR) || + (rv == NS_ERROR_FAILURE) || + (rv == NS_ERROR_OUT_OF_MEMORY)) + return rv; + else + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +/* AString createFilter (in unsigned long aMaxSize, in AString aPattern, in AString aPrefix, in AString aSuffix, in AString aAttr, in AString aValue); */ +NS_IMETHODIMP nsLDAPService::CreateFilter(uint32_t aMaxSize, + const nsACString & aPattern, + const nsACString & aPrefix, + const nsACString & aSuffix, + const nsACString & aAttr, + const nsACString & aValue, + nsACString & _retval) +{ + if (!aMaxSize) { + return NS_ERROR_INVALID_ARG; + } + + // figure out how big of an array we're going to need for the tokens, + // including a trailing NULL, and allocate space for it. + // + const char *iter = aValue.BeginReading(); + const char *iterEnd = aValue.EndReading(); + uint32_t numTokens = CountTokens(iter, iterEnd); + char **valueWords; + valueWords = static_cast(moz_xmalloc((numTokens + 1) * + sizeof(char *))); + if (!valueWords) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // build the array of values + // + uint32_t curToken = 0; + while (iter != iterEnd && curToken < numTokens ) { + valueWords[curToken] = NextToken(&iter, &iterEnd); + if ( !valueWords[curToken] ) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(curToken, valueWords); + return NS_ERROR_OUT_OF_MEMORY; + } + curToken++; + } + valueWords[numTokens] = 0; // end of array signal to LDAP C SDK + + // make buffer to be used for construction + // + char *buffer = static_cast(moz_xmalloc(aMaxSize * sizeof(char))); + if (!buffer) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords); + return NS_ERROR_OUT_OF_MEMORY; + } + + // create the filter itself + // + nsresult rv; + int result = ldap_create_filter(buffer, aMaxSize, + const_cast(PromiseFlatCString(aPattern).get()), + const_cast(PromiseFlatCString(aPrefix).get()), + const_cast(PromiseFlatCString(aSuffix).get()), + const_cast(PromiseFlatCString(aAttr).get()), + const_cast(PromiseFlatCString(aValue).get()), + valueWords); + switch (result) { + case LDAP_SUCCESS: + rv = NS_OK; + break; + + case LDAP_SIZELIMIT_EXCEEDED: + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPService::CreateFilter(): " + "filter longer than max size of %d generated", + aMaxSize)); + rv = NS_ERROR_NOT_AVAILABLE; + break; + + case LDAP_PARAM_ERROR: + rv = NS_ERROR_INVALID_ARG; + break; + + default: + NS_ERROR("nsLDAPService::CreateFilter(): ldap_create_filter() " + "returned unexpected error"); + rv = NS_ERROR_UNEXPECTED; + break; + } + + _retval.Assign(buffer); + + // done with the array and the buffer + // + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords); + free(buffer); + + return rv; +} + +// Parse a distinguished name (DN) and returns the relative DN, +// base DN and the list of attributes that make up the relative DN. +NS_IMETHODIMP nsLDAPService::ParseDn(const char *aDn, + nsACString &aRdn, + nsACString &aBaseDn, + uint32_t *aRdnCount, + char ***aRdnAttrs) +{ + NS_ENSURE_ARG_POINTER(aRdnCount); + NS_ENSURE_ARG_POINTER(aRdnAttrs); + + // explode the DN + char **dnComponents = ldap_explode_dn(aDn, 0); + if (!dnComponents) { + NS_ERROR("nsLDAPService::ParseDn: parsing DN failed"); + return NS_ERROR_UNEXPECTED; + } + + // count DN components + if (!*dnComponents || !*(dnComponents + 1)) { + NS_ERROR("nsLDAPService::ParseDn: DN has too few components"); + ldap_value_free(dnComponents); + return NS_ERROR_UNEXPECTED; + } + + // get the base DN + nsAutoCString baseDn(nsDependentCString(*(dnComponents + 1))); + for (char **component = dnComponents + 2; *component; ++component) { + baseDn.AppendLiteral(","); + baseDn.Append(nsDependentCString(*component)); + } + + // explode the RDN + char **rdnComponents = ldap_explode_rdn(*dnComponents, 0); + if (!rdnComponents) { + NS_ERROR("nsLDAPService::ParseDn: parsing RDN failed"); + ldap_value_free(dnComponents); + return NS_ERROR_UNEXPECTED; + } + + // count RDN attributes + uint32_t rdnCount = 0; + for (char **component = rdnComponents; *component; ++component) + ++rdnCount; + if (rdnCount < 1) { + NS_ERROR("nsLDAPService::ParseDn: RDN has too few components"); + ldap_value_free(dnComponents); + ldap_value_free(rdnComponents); + return NS_ERROR_UNEXPECTED; + } + + // get the RDN attribute names + char **attrNameArray = static_cast( + moz_xmalloc(rdnCount * sizeof(char *))); + if (!attrNameArray) { + NS_ERROR("nsLDAPService::ParseDn: out of memory "); + ldap_value_free(dnComponents); + ldap_value_free(rdnComponents); + return NS_ERROR_OUT_OF_MEMORY; + } + uint32_t index = 0; + for (char **component = rdnComponents; *component; ++component) { + uint32_t len = 0; + char *p; + for (p = *component; *p != '\0' && *p != '='; ++p) + ++len; + if (*p != '=') { + NS_ERROR("nsLDAPService::parseDn: " + "could not find '=' in RDN component"); + ldap_value_free(dnComponents); + ldap_value_free(rdnComponents); + return NS_ERROR_UNEXPECTED; + } + if (!(attrNameArray[index] = (char*)NS_Alloc(len + 1))) { + NS_ERROR("nsLDAPService::ParseDn: out of memory "); + ldap_value_free(dnComponents); + ldap_value_free(rdnComponents); + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, attrNameArray); + return NS_ERROR_OUT_OF_MEMORY; + } + memcpy(attrNameArray[index], *component, len); + *(attrNameArray[index] + len) = '\0'; + ++index; + } + + // perform assignments + aRdn.Assign(*dnComponents); + aBaseDn.Assign(baseDn); + *aRdnCount = rdnCount; + *aRdnAttrs = attrNameArray; + + ldap_value_free(dnComponents); + ldap_value_free(rdnComponents); + return NS_OK; +} + +// Count the number of space-separated tokens between aIter and aIterEnd +// +uint32_t +nsLDAPService::CountTokens(const char *aIter, + const char *aIterEnd) +{ + uint32_t count(0); + + // keep iterating through the string until we hit the end + // + while (aIter != aIterEnd) { + + // move past any leading spaces + // + while (aIter != aIterEnd && + ldap_utf8isspace(const_cast(aIter))){ + ++aIter; + } + + // move past all chars in this token + // + while (aIter != aIterEnd) { + + if (ldap_utf8isspace(const_cast(aIter))) { + ++count; // token finished; increment the count + ++aIter; // move past the space + break; + } + + ++aIter; // move to next char + + // if we've hit the end of this token and the end of this + // iterator simultaneous, be sure to bump the count, since we're + // never gonna hit the IsAsciiSpace where it's normally done. + // + if (aIter == aIterEnd) { + ++count; + } + + } + } + + return count; +} + +// return the next token in this iterator +// +char* +nsLDAPService::NextToken(const char **aIter, + const char **aIterEnd) +{ + // move past any leading whitespace + // + while (*aIter != *aIterEnd && + ldap_utf8isspace(const_cast(*aIter))) { + ++(*aIter); + } + + const char *start = *aIter; + + // copy the token into our local variable + // + while (*aIter != *aIterEnd && + !ldap_utf8isspace(const_cast(*aIter))) { + ++(*aIter); + } + + return ToNewCString(Substring(start, *aIter)); +} diff --git a/ldap/xpcom/src/nsLDAPService.h b/ldap/xpcom/src/nsLDAPService.h new file mode 100644 index 0000000000..2b0f4ef4ee --- /dev/null +++ b/ldap/xpcom/src/nsLDAPService.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ldap.h" +#include "nsStringGlue.h" +#include "nsCOMArray.h" +#include "nsDataHashtable.h" +#include "nsILDAPService.h" +#include "nsILDAPMessage.h" +#include "nsILDAPMessageListener.h" +#include "nsCOMPtr.h" +#include "nsILDAPServer.h" +#include "nsILDAPConnection.h" +#include "nsILDAPMessage.h" +#include "mozilla/Mutex.h" + +// 6a89ae33-7a90-430d-888c-0dede53a951a +// +#define NS_LDAPSERVICE_CID \ +{ \ + 0x6a89ae33, 0x7a90, 0x430d, \ + {0x88, 0x8c, 0x0d, 0xed, 0xe5, 0x3a, 0x95, 0x1a} \ +} + +// This is a little "helper" class, we use to store information +// related to one Service entry (one LDAP server). +// +class nsLDAPServiceEntry +{ + public: + nsLDAPServiceEntry(); + virtual ~nsLDAPServiceEntry() {}; + bool Init(); + + inline uint32_t GetLeases(); + inline void IncrementLeases(); + inline bool DecrementLeases(); + + inline PRTime GetTimestamp(); + inline void SetTimestamp(); + + inline already_AddRefed GetServer(); + inline bool SetServer(nsILDAPServer *aServer); + + inline already_AddRefed GetConnection(); + inline void SetConnection(nsILDAPConnection *aConnection); + + inline already_AddRefed GetMessage(); + inline void SetMessage(nsILDAPMessage *aMessage); + + inline already_AddRefed PopListener(); + inline bool PushListener(nsILDAPMessageListener *); + + inline bool IsRebinding(); + inline void SetRebinding(bool); + + inline bool DeleteEntry(); + + protected: + uint32_t mLeases; // The number of leases currently granted + PRTime mTimestamp; // Last time this server was "used" + bool mDelete; // This entry is due for deletion + bool mRebinding; // Keep state if we are rebinding or not + + nsCOMPtr mServer; + nsCOMPtr mConnection; + nsCOMPtr mMessage; + + // Array holding all the pending callbacks (listeners) for this entry + nsCOMArray mListeners; +}; + +// This is the interface we're implementing. +// +class nsLDAPService : public nsILDAPService, public nsILDAPMessageListener +{ + public: + // interface decls + // + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPSERVICE + NS_DECL_NSILDAPMESSAGELISTENER + + // constructor and destructor + // + nsLDAPService(); + + nsresult Init(); + + protected: + virtual ~nsLDAPService(); + nsresult EstablishConnection(nsLDAPServiceEntry *, + nsILDAPMessageListener *); + + // kinda like strtok_r, but with iterators. for use by + // createFilter + // + char *NextToken(const char **aIter, const char **aIterEnd); + + // count how many tokens are in this string; for use by + // createFilter; note that unlike with NextToken, these params + // are copies, not references. + // + uint32_t CountTokens(const char * aIter, const char * aIterEnd); + + + mozilla::Mutex mLock; // Lock mechanism + + // Hash table holding server entries + nsDataHashtable mServers; + // Hash table holding "reverse" lookups from connection to server + nsDataHashtable mConnections; +}; diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.cpp b/ldap/xpcom/src/nsLDAPSyncQuery.cpp new file mode 100644 index 0000000000..e1f70ee7fa --- /dev/null +++ b/ldap/xpcom/src/nsLDAPSyncQuery.cpp @@ -0,0 +1,386 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPSyncQuery.h" +#include "nsIServiceManager.h" +#include "nsILDAPErrors.h" +#include "nsThreadUtils.h" +#include "nsILDAPMessage.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsMemory.h" + +// nsISupports Implementation + +NS_IMPL_ISUPPORTS(nsLDAPSyncQuery, nsILDAPSyncQuery, nsILDAPMessageListener) + +// Constructor +// +nsLDAPSyncQuery::nsLDAPSyncQuery() : + mFinished(false), // This is a control variable for event loop + mProtocolVersion(nsILDAPConnection::VERSION3) +{ +} + +// Destructor +// +nsLDAPSyncQuery::~nsLDAPSyncQuery() +{ +} + + +// Messages received are passed back via this function. +// void OnLDAPMessage (in nsILDAPMessage aMessage) +// +NS_IMETHODIMP +nsLDAPSyncQuery::OnLDAPMessage(nsILDAPMessage *aMessage) +{ + int32_t messageType; + + // just in case. + // + if (!aMessage) { + return NS_OK; + } + + // figure out what sort of message was returned + // + nsresult rv = aMessage->GetType(&messageType); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected " + "error in aMessage->GetType()"); + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + switch (messageType) { + + case nsILDAPMessage::RES_BIND: + + // a bind has completed + // + return OnLDAPBind(aMessage); + + case nsILDAPMessage::RES_SEARCH_ENTRY: + + // a search entry has been returned + // + return OnLDAPSearchEntry(aMessage); + + case nsILDAPMessage::RES_SEARCH_RESULT: + + // the search is finished; we're all done + // + return OnLDAPSearchResult(aMessage); + + default: + + // Given the LDAP operations nsLDAPSyncQuery uses, we should + // never get here. If we do get here in a release build, it's + // probably a bug, but maybe it's the LDAP server doing something + // weird. Might as well try and continue anyway. The session should + // eventually get reaped by the timeout code, if necessary. + // + NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected " + "LDAP message received"); + return NS_OK; + } +} + +// void onLDAPInit (in nsresult aStatus); +// +NS_IMETHODIMP +nsLDAPSyncQuery::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus) +{ + nsresult rv; // temp for xpcom return values + // create and initialize an LDAP operation (to be used for the bind) + // + mOperation = do_CreateInstance("@mozilla.org/network/ldap-operation;1", + &rv); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + // our OnLDAPMessage accepts all result callbacks + // + rv = mOperation->Init(mConnection, this, nullptr); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; // this should never happen + } + + // kick off a bind operation + // + rv = mOperation->SimpleBind(EmptyCString()); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsLDAPSyncQuery::OnLDAPBind(nsILDAPMessage *aMessage) +{ + + int32_t errCode; + + mOperation = nullptr; // done with bind op; make nsCOMPtr release it + + // get the status of the bind + // + nsresult rv = aMessage->GetErrorCode(&errCode); + if (NS_FAILED(rv)) { + + NS_ERROR("nsLDAPSyncQuery::OnLDAPBind(): couldn't get " + "error code from aMessage"); + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + + // check to be sure the bind succeeded + // + if (errCode != nsILDAPErrors::SUCCESS) { + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + // ok, we're starting a search + // + return StartLDAPSearch(); +} + +nsresult +nsLDAPSyncQuery::OnLDAPSearchEntry(nsILDAPMessage *aMessage) +{ + uint32_t attrCount; + char** attributes; + nsresult rv = aMessage->GetAttributes(&attrCount, &attributes); + if (NS_FAILED(rv)) + { + NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): " + "aMessage->GetAttributes() failed"); + FinishLDAPQuery(); + return rv; + } + + // Iterate through the attributes received in this message + for (uint32_t i = 0; i < attrCount; i++) + { + char16_t **vals; + uint32_t valueCount; + + // Get the values of this attribute. + // XXX better failure handling + rv = aMessage->GetValues(attributes[i], &valueCount, &vals); + if (NS_FAILED(rv)) + { + NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): " + "aMessage->GetValues() failed\n"); + FinishLDAPQuery(); + break; + } + + // Store all values of this attribute in the mResults. + for (uint32_t j = 0; j < valueCount; j++) { + mResults.Append(char16_t('\n')); + mResults.AppendASCII(attributes[i]); + mResults.Append(char16_t('=')); + mResults.Append(vals[j]); + } + + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(valueCount, vals); + } + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(attrCount, attributes); + + return rv; +} + + +nsresult +nsLDAPSyncQuery::OnLDAPSearchResult(nsILDAPMessage *aMessage) +{ + // We are done with the LDAP search. + // Release the control variable for the eventloop and other members + // + FinishLDAPQuery(); + return NS_OK; +} + +nsresult +nsLDAPSyncQuery::StartLDAPSearch() +{ + nsresult rv; + // create and initialize an LDAP operation (to be used for the search + // + mOperation = + do_CreateInstance("@mozilla.org/network/ldap-operation;1", &rv); + + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't " + "create @mozilla.org/network/ldap-operation;1"); + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + // initialize the LDAP operation object + // + rv = mOperation->Init(mConnection, this, nullptr); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't " + "initialize LDAP operation"); + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + // get the search filter associated with the directory server url; + // + nsAutoCString urlFilter; + rv = mServerURL->GetFilter(urlFilter); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + // get the base dn to search + // + nsAutoCString dn; + rv = mServerURL->GetDn(dn); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + // and the scope + // + int32_t scope; + rv = mServerURL->GetScope(&scope); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + nsAutoCString attributes; + rv = mServerURL->GetAttributes(attributes); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; + } + + // time to kick off the search. + rv = mOperation->SearchExt(dn, scope, urlFilter, attributes, 0, 0); + + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +// void initConnection (); +// +nsresult nsLDAPSyncQuery::InitConnection() +{ + // Because mConnection->Init proxies back to the main thread, this + // better be the main thread. + NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE); + nsresult rv; // temp for xpcom return values + // create an LDAP connection + // + mConnection = do_CreateInstance("@mozilla.org/network/ldap-connection;1", + &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPSyncQuery::InitConnection(): could " + "not create @mozilla.org/network/ldap-connection;1"); + FinishLDAPQuery(); + return NS_ERROR_FAILURE; + } + + // have we been properly initialized? + // + if (!mServerURL) { + NS_ERROR("nsLDAPSyncQuery::InitConnection(): mServerURL " + "is NULL"); + FinishLDAPQuery(); + return NS_ERROR_NOT_INITIALIZED; + } + rv = mConnection->Init(mServerURL, EmptyCString(), this, + nullptr, mProtocolVersion); + if (NS_FAILED(rv)) { + FinishLDAPQuery(); + return NS_ERROR_UNEXPECTED; // this should never happen + } + + return NS_OK; +} + +void +nsLDAPSyncQuery::FinishLDAPQuery() +{ + // We are done with the LDAP operation. + // Release the Control variable for the eventloop + // + mFinished = true; + + // Release member variables + // + mConnection = nullptr; + mOperation = nullptr; + mServerURL = nullptr; + +} + +/* wstring getQueryResults (in nsILDAPURL aServerURL, in unsigned long aVersion); */ +NS_IMETHODIMP nsLDAPSyncQuery::GetQueryResults(nsILDAPURL *aServerURL, + uint32_t aProtocolVersion, + char16_t **_retval) +{ + nsresult rv; + + if (!aServerURL) { + NS_ERROR("nsLDAPSyncQuery::GetQueryResults() called without LDAP URL"); + return NS_ERROR_FAILURE; + } + mServerURL = aServerURL; + mProtocolVersion = aProtocolVersion; + + nsCOMPtr currentThread = do_GetCurrentThread(); + + // Start an LDAP query. + // InitConnection will bind to the ldap server and post a OnLDAPMessage + // event. This event will trigger a search and the whole operation will + // be carried out by chain of events + // + rv = InitConnection(); + if (NS_FAILED(rv)) + return rv; + + // We want this LDAP query to be synchronous while the XPCOM LDAP is + // async in nature. So this eventQueue handling will wait for the + // LDAP operation to be finished. + // mFinished controls the state of the LDAP opertion. + // It will be released in any case (success/failure) + + + // Run the event loop, + // mFinished is a control variable + // + while (!mFinished) + NS_ENSURE_STATE(NS_ProcessNextEvent(currentThread)); + + // Return results + // + if (!mResults.IsEmpty()) { + *_retval = ToNewUnicode(mResults); + if (!_retval) + rv = NS_ERROR_OUT_OF_MEMORY; + } + return rv; + +} diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.h b/ldap/xpcom/src/nsLDAPSyncQuery.h new file mode 100644 index 0000000000..78cfd43a49 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPSyncQuery.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsILDAPConnection.h" +#include "nsILDAPOperation.h" +#include "nsILDAPMessageListener.h" +#include "nsILDAPURL.h" +#include "nsStringGlue.h" +#include "nsILDAPSyncQuery.h" + +// DDDEE14E-ED81-4182-9323-C2AB22FBA68E +#define NS_LDAPSYNCQUERY_CID \ +{ 0xdddee14e, 0xed81, 0x4182, \ + { 0x93, 0x23, 0xc2, 0xab, 0x22, 0xfb, 0xa6, 0x8e }} + + +class nsLDAPSyncQuery : public nsILDAPSyncQuery, + public nsILDAPMessageListener + +{ + public: + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILDAPMESSAGELISTENER + NS_DECL_NSILDAPSYNCQUERY + + nsLDAPSyncQuery(); + + protected: + virtual ~nsLDAPSyncQuery(); + + nsCOMPtr mConnection; // connection used for search + nsCOMPtr mOperation; // current ldap op + nsCOMPtr mServerURL; // LDAP URL + bool mFinished; // control variable for eventQ + nsString mResults; // values to return + uint32_t mProtocolVersion; // LDAP version to use + + nsresult InitConnection(); + // check that we bound ok and start then call StartLDAPSearch + nsresult OnLDAPBind(nsILDAPMessage *aMessage); + + // add to the results set + nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage); + + + nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage); + + // kick off a search + nsresult StartLDAPSearch(); + + // Clean up after the LDAP Query is done. + void FinishLDAPQuery(); +}; + diff --git a/ldap/xpcom/src/nsLDAPURL.cpp b/ldap/xpcom/src/nsLDAPURL.cpp new file mode 100644 index 0000000000..3757438486 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPURL.cpp @@ -0,0 +1,725 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsLDAPURL.h" +#include "netCore.h" +#include "plstr.h" +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "nsComponentManagerUtils.h" +#include "nsIStandardURL.h" +#include "nsMsgUtils.h" + +// The two schemes we support, LDAP and LDAPS +// +NS_NAMED_LITERAL_CSTRING(LDAP_SCHEME, "ldap"); +NS_NAMED_LITERAL_CSTRING(LDAP_SSL_SCHEME, "ldaps"); + +NS_IMPL_ISUPPORTS(nsLDAPURL, nsILDAPURL, nsIURI) + +nsLDAPURL::nsLDAPURL() + : mScope(SCOPE_BASE), + mOptions(0) +{ +} + +nsLDAPURL::~nsLDAPURL() +{ +} + +nsresult +nsLDAPURL::Init(uint32_t aUrlType, int32_t aDefaultPort, + const nsACString &aSpec, const char* aOriginCharset, + nsIURI *aBaseURI) +{ + if (!mBaseURL) + { + mBaseURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID); + if (!mBaseURL) + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv; + nsCOMPtr standardURL(do_QueryInterface(mBaseURL, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = standardURL->Init(aUrlType, aDefaultPort, aSpec, aOriginCharset, + aBaseURI); + NS_ENSURE_SUCCESS(rv, rv); + + // Now get the spec from the mBaseURL in case it was a relative one + nsCString spec; + rv = mBaseURL->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + return SetSpec(spec); +} + +void +nsLDAPURL::GetPathInternal(nsCString &aPath) +{ + aPath.Assign('/'); + + if (!mDN.IsEmpty()) + aPath.Append(mDN); + + if (!mAttributes.IsEmpty()) + aPath.Append('?'); + + // If mAttributes isn't empty, cut off the internally stored commas at start + // and end, and append to the path. + if (!mAttributes.IsEmpty()) + aPath.Append(Substring(mAttributes, 1, mAttributes.Length() - 2)); + + if (mScope || !mFilter.IsEmpty()) + { + aPath.Append((mAttributes.IsEmpty() ? "??" : "?")); + if (mScope) + { + if (mScope == SCOPE_ONELEVEL) + aPath.Append("one"); + else if (mScope == SCOPE_SUBTREE) + aPath.Append("sub"); + } + if (!mFilter.IsEmpty()) + { + aPath.Append('?'); + aPath.Append(mFilter); + } + } +} + +nsresult +nsLDAPURL::SetPathInternal(const nsCString &aPath) +{ + LDAPURLDesc *desc; + + // This is from the LDAP C-SDK, which currently doesn't + // support everything from RFC 2255... :( + // + int err = ldap_url_parse(aPath.get(), &desc); + switch (err) { + case LDAP_SUCCESS: { + // The base URL can pick up the host & port details and deal with them + // better than we can + mDN = desc->lud_dn; + mScope = desc->lud_scope; + mFilter = desc->lud_filter; + mOptions = desc->lud_options; + nsresult rv = SetAttributeArray(desc->lud_attrs); + if (NS_FAILED(rv)) + return rv; + + ldap_free_urldesc(desc); + return NS_OK; + } + + case LDAP_URL_ERR_NOTLDAP: + case LDAP_URL_ERR_NODN: + case LDAP_URL_ERR_BADSCOPE: + return NS_ERROR_MALFORMED_URI; + + case LDAP_URL_ERR_MEM: + NS_ERROR("nsLDAPURL::SetSpec: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + + case LDAP_URL_ERR_PARAM: + return NS_ERROR_INVALID_POINTER; + } + + // This shouldn't happen... + return NS_ERROR_UNEXPECTED; +} + +// A string representation of the URI. Setting the spec +// causes the new spec to be parsed, initializing the URI. Setting +// the spec (or any of the accessors) causes also any currently +// open streams on the URI's channel to be closed. + +NS_IMETHODIMP +nsLDAPURL::GetSpec(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetSpec(_retval); +} + +NS_IMETHODIMP +nsLDAPURL::SetSpec(const nsACString &aSpec) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + // Cache the original spec in case we don't like what we've been passed and + // need to reset ourselves. + nsCString originalSpec; + nsresult rv = mBaseURL->GetSpec(originalSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mBaseURL->SetSpec(aSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SetPathInternal(PromiseFlatCString(aSpec)); + if (NS_FAILED(rv)) + mBaseURL->SetSpec(originalSpec); + + return rv; +} + +NS_IMETHODIMP nsLDAPURL::GetPrePath(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetPrePath(_retval); +} + +NS_IMETHODIMP nsLDAPURL::GetScheme(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetScheme(_retval); +} + +NS_IMETHODIMP nsLDAPURL::SetScheme(const nsACString &aScheme) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + if (aScheme.Equals(LDAP_SCHEME, nsCaseInsensitiveCStringComparator())) + mOptions &= !OPT_SECURE; + else if (aScheme.Equals(LDAP_SSL_SCHEME, + nsCaseInsensitiveCStringComparator())) + mOptions |= OPT_SECURE; + else + return NS_ERROR_MALFORMED_URI; + + return mBaseURL->SetScheme(aScheme); +} + +NS_IMETHODIMP +nsLDAPURL::GetUserPass(nsACString &_retval) +{ + _retval.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::SetUserPass(const nsACString &aUserPass) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::GetUsername(nsACString &_retval) +{ + _retval.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::SetUsername(const nsACString &aUsername) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::GetPassword(nsACString &_retval) +{ + _retval.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::SetPassword(const nsACString &aPassword) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::GetHostPort(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetHostPort(_retval); +} + +NS_IMETHODIMP +nsLDAPURL::SetHostPort(const nsACString &aHostPort) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->SetHostPort(aHostPort); +} + +NS_IMETHODIMP +nsLDAPURL::SetHostAndPort(const nsACString &aHostPort) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->SetHostAndPort(aHostPort); +} + +NS_IMETHODIMP +nsLDAPURL::GetHost(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetHost(_retval); +} + +NS_IMETHODIMP +nsLDAPURL::SetHost(const nsACString &aHost) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->SetHost(aHost); +} + +NS_IMETHODIMP +nsLDAPURL::GetPort(int32_t *_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetPort(_retval); +} + +NS_IMETHODIMP +nsLDAPURL::SetPort(int32_t aPort) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->SetPort(aPort); +} + +NS_IMETHODIMP nsLDAPURL::GetPath(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetPath(_retval); +} + +NS_IMETHODIMP nsLDAPURL::SetPath(const nsACString &aPath) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv = SetPathInternal(PromiseFlatCString(aPath)); + NS_ENSURE_SUCCESS(rv, rv); + + return mBaseURL->SetPath(aPath); +} + +NS_IMETHODIMP nsLDAPURL::GetAsciiSpec(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + // XXX handle extra items? + return mBaseURL->GetAsciiSpec(_retval); +} + +NS_IMETHODIMP nsLDAPURL::GetAsciiHost(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetAsciiHost(_retval); +} + +NS_IMETHODIMP +nsLDAPURL::GetAsciiHostPort(nsACString &_retval) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetAsciiHostPort(_retval); +} + +NS_IMETHODIMP nsLDAPURL::GetOriginCharset(nsACString &result) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->GetOriginCharset(result); +} + +// boolean equals (in nsIURI other) +// (based on nsSimpleURI::Equals) +NS_IMETHODIMP nsLDAPURL::Equals(nsIURI *other, bool *_retval) +{ + *_retval = false; + if (other) + { + nsresult rv; + nsCOMPtr otherURL(do_QueryInterface(other, &rv)); + if (NS_SUCCEEDED(rv)) + { + nsAutoCString thisSpec, otherSpec; + uint32_t otherOptions; + + rv = GetSpec(thisSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = otherURL->GetSpec(otherSpec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = otherURL->GetOptions(&otherOptions); + NS_ENSURE_SUCCESS(rv, rv); + + if (thisSpec == otherSpec && mOptions == otherOptions) + *_retval = true; + } + } + return NS_OK; +} + +// boolean schemeIs(in const char * scheme); +// +NS_IMETHODIMP nsLDAPURL::SchemeIs(const char *aScheme, bool *aEquals) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + return mBaseURL->SchemeIs(aScheme, aEquals); +} + +// nsIURI clone (); +// +nsresult +nsLDAPURL::CloneInternal(RefHandlingEnum aRefHandlingMode, + const nsACString& newRef, nsIURI** aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsLDAPURL *clone = new nsLDAPURL(); + + if (!clone) + return NS_ERROR_OUT_OF_MEMORY; + + clone->mDN = mDN; + clone->mScope = mScope; + clone->mFilter = mFilter; + clone->mOptions = mOptions; + clone->mAttributes = mAttributes; + + nsresult rv; + if (aRefHandlingMode == eHonorRef) { + rv = mBaseURL->Clone(getter_AddRefs(clone->mBaseURL)); + } else if (aRefHandlingMode == eReplaceRef) { + rv = mBaseURL->CloneWithNewRef(newRef, getter_AddRefs(clone->mBaseURL)); + } else { + rv = mBaseURL->CloneIgnoringRef(getter_AddRefs(clone->mBaseURL)); + } + NS_ENSURE_SUCCESS(rv, rv); + + NS_ADDREF(*aResult = clone); + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPURL::Clone(nsIURI** result) +{ + return CloneInternal(eHonorRef, EmptyCString(), result); +} + +NS_IMETHODIMP +nsLDAPURL::CloneIgnoringRef(nsIURI** result) +{ + return CloneInternal(eIgnoreRef, EmptyCString(), result); +} + +NS_IMETHODIMP +nsLDAPURL::CloneWithNewRef(const nsACString& newRef, nsIURI** result) +{ + return CloneInternal(eReplaceRef, newRef, result); +} + +// string resolve (in string relativePath); +// +NS_IMETHODIMP nsLDAPURL::Resolve(const nsACString &relativePath, + nsACString &_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// The following attributes come from nsILDAPURL + +// attribute AUTF8String dn; +// +NS_IMETHODIMP nsLDAPURL::GetDn(nsACString& _retval) +{ + _retval.Assign(mDN); + return NS_OK; +} +NS_IMETHODIMP nsLDAPURL::SetDn(const nsACString& aDn) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + mDN.Assign(aDn); + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +NS_IMETHODIMP nsLDAPURL::GetAttributes(nsACString &aAttributes) +{ + if (mAttributes.IsEmpty()) + { + aAttributes.Truncate(); + return NS_OK; + } + + NS_ASSERTION(mAttributes[0] == ',' && + mAttributes[mAttributes.Length() - 1] == ',', + "mAttributes does not begin and end with a comma"); + + // We store the string internally with comma before and after, so strip + // them off here. + aAttributes = Substring(mAttributes, 1, mAttributes.Length() - 2); + return NS_OK; +} + +NS_IMETHODIMP nsLDAPURL::SetAttributes(const nsACString &aAttributes) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + if (aAttributes.IsEmpty()) + mAttributes.Truncate(); + else + { + // We need to make sure we start off the string with a comma. + if (aAttributes[0] != ',') + mAttributes = ','; + + mAttributes.Append(aAttributes); + + // Also end with a comma if appropriate. + if (mAttributes[mAttributes.Length() - 1] != ',') + mAttributes.Append(','); + } + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +nsresult nsLDAPURL::SetAttributeArray(char** aAttributes) +{ + mAttributes.Truncate(); + + while (aAttributes && *aAttributes) + { + // Always start with a comma as that's what we store internally. + mAttributes.Append(','); + mAttributes.Append(*aAttributes); + ++aAttributes; + } + + // Add a comma on the end if we have something. + if (!mAttributes.IsEmpty()) + mAttributes.Append(','); + + return NS_OK; +} + +NS_IMETHODIMP nsLDAPURL::AddAttribute(const nsACString &aAttribute) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + if (mAttributes.IsEmpty()) + { + mAttributes = ','; + mAttributes.Append(aAttribute); + mAttributes.Append(','); + } + else + { + // Wrap the attribute in commas, so that we can do an exact match. + nsAutoCString findAttribute(","); + findAttribute.Append(aAttribute); + findAttribute.Append(','); + + // Check to see if the attribute is already stored. If it is, then also + // check to see if it is the last attribute in the string, or if the next + // character is a comma, this means we won't match substrings. + int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare); + if (pos != -1) + return NS_OK; + + mAttributes.Append(Substring(findAttribute, 1)); + } + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +NS_IMETHODIMP nsLDAPURL::RemoveAttribute(const nsACString &aAttribute) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + if (mAttributes.IsEmpty()) + return NS_OK; + + nsAutoCString findAttribute(","); + findAttribute.Append(aAttribute); + findAttribute.Append(','); + + if (mAttributes.Equals(findAttribute, nsCaseInsensitiveCStringComparator())) + mAttributes.Truncate(); + else + { + int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare); + if (pos == -1) + return NS_OK; + + mAttributes.Cut(pos, findAttribute.Length() - 1); + } + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +NS_IMETHODIMP nsLDAPURL::HasAttribute(const nsACString &aAttribute, + bool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + nsAutoCString findAttribute(","); + findAttribute.Append(aAttribute); + findAttribute.Append(','); + + *_retval = mAttributes.Find(findAttribute, CaseInsensitiveCompare) != -1; + return NS_OK; +} + +NS_IMETHODIMP nsLDAPURL::GetScope(int32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = mScope; + return NS_OK; +} + +NS_IMETHODIMP nsLDAPURL::SetScope(int32_t aScope) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + // Only allow scopes supported by the C-SDK + if ((aScope != SCOPE_BASE) && (aScope != SCOPE_ONELEVEL) && + (aScope != SCOPE_SUBTREE)) + return NS_ERROR_MALFORMED_URI; + + mScope = aScope; + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +NS_IMETHODIMP nsLDAPURL::GetFilter(nsACString& _retval) +{ + _retval.Assign(mFilter); + return NS_OK; +} +NS_IMETHODIMP nsLDAPURL::SetFilter(const nsACString& aFilter) +{ + if (!mBaseURL) + return NS_ERROR_NOT_INITIALIZED; + + mFilter.Assign(aFilter); + + if (mFilter.IsEmpty()) + mFilter.AssignLiteral("(objectclass=*)"); + + // Now get the current path + nsCString newPath; + GetPathInternal(newPath); + + // and update the base url + return mBaseURL->SetPath(newPath); +} + +NS_IMETHODIMP nsLDAPURL::GetOptions(uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = mOptions; + return NS_OK; +} + +NS_IMETHODIMP nsLDAPURL::SetOptions(uint32_t aOptions) +{ + // Secure is the only option supported at the moment + if ((mOptions & OPT_SECURE) == (aOptions & OPT_SECURE)) + return NS_OK; + + mOptions = aOptions; + + if ((aOptions & OPT_SECURE) == OPT_SECURE) + return SetScheme(LDAP_SSL_SCHEME); + + return SetScheme(LDAP_SCHEME); +} + +NS_IMETHODIMP nsLDAPURL::SetRef(const nsACString &aRef) +{ + return mBaseURL->SetRef(aRef); +} + +NS_IMETHODIMP +nsLDAPURL::GetRef(nsACString &result) +{ + return mBaseURL->GetRef(result); +} + +NS_IMETHODIMP nsLDAPURL::EqualsExceptRef(nsIURI *other, bool *result) +{ + return mBaseURL->EqualsExceptRef(other, result); +} + +NS_IMETHODIMP +nsLDAPURL::GetSpecIgnoringRef(nsACString &result) +{ + return mBaseURL->GetSpecIgnoringRef(result); +} + +NS_IMETHODIMP +nsLDAPURL::GetHasRef(bool *result) +{ + return mBaseURL->GetHasRef(result); +} diff --git a/ldap/xpcom/src/nsLDAPURL.h b/ldap/xpcom/src/nsLDAPURL.h new file mode 100644 index 0000000000..b80da957dd --- /dev/null +++ b/ldap/xpcom/src/nsLDAPURL.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ldap.h" +#include "nsStringGlue.h" +#include "nsILDAPURL.h" +#include "nsCOMPtr.h" + +// cb7c67f8-0053-4072-89e9-501cbd1b35ab +#define NS_LDAPURL_CID \ +{ 0xcb7c67f8, 0x0053, 0x4072, \ + { 0x89, 0xe9, 0x50, 0x1c, 0xbd, 0x1b, 0x35, 0xab}} + +/** + * nsLDAPURL + * + * nsLDAPURL uses an nsStandardURL stored in mBaseURL as its main url formatter. + * + * This is done to ensure that the pre-path sections of the URI are correctly + * formatted and to re-use the functions for nsIURI as appropriate. + * + * Handling of the path sections of the URI are done within nsLDAPURL/parts of + * the LDAP c-sdk. nsLDAPURL holds the individual sections of the path of the + * URI locally (to allow convenient get/set), but always updates the mBaseURL + * when one changes to ensure that mBaseURL.spec and the local data are kept + * consistent. + */ + +class nsLDAPURL : public nsILDAPURL +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIURI + NS_DECL_NSILDAPURL + + nsLDAPURL(); + +protected: + enum RefHandlingEnum { + eIgnoreRef, + eHonorRef, + eReplaceRef + }; + virtual ~nsLDAPURL(); + + void GetPathInternal(nsCString &aPath); + nsresult SetPathInternal(const nsCString &aPath); + nsresult SetAttributeArray(char** aAttributes); + nsresult CloneInternal(RefHandlingEnum aRefHandlingMode, + const nsACString& newRef, nsIURI** aResult); + + nsCString mDN; // Base Distinguished Name (Base DN) + int32_t mScope; // Search scope (base, one or sub) + nsCString mFilter; // LDAP search filter + uint32_t mOptions; // Options + nsCString mAttributes; + nsCOMPtr mBaseURL; +}; diff --git a/ldap/xpcom/src/nsLDAPUtils.h b/ldap/xpcom/src/nsLDAPUtils.h new file mode 100644 index 0000000000..87f5dacc1f --- /dev/null +++ b/ldap/xpcom/src/nsLDAPUtils.h @@ -0,0 +1,147 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This module contains helper functions and macros for converting directory + module to frozen linkage. + */ +#include "nsServiceManagerUtils.h" +#include "nsStringGlue.h" +#include + +#ifdef MOZILLA_INTERNAL_API +/* Internal API helper macros */ + +#define LdapCompressWhitespace(str) \ + (str).CompressWhitespace() + +#else // MOZILLA_INTERNAL_API +/* Frozen linkage helper functions */ + +/* This macro has been copied from msgcore.h */ +#define IS_SPACE(VAL) \ + (((((PRIntn)(VAL)) & 0x7f) == ((PRIntn)(VAL))) && isspace((PRIntn)(VAL))) + +/* This function has been copied from nsMsgUtils.cpp */ +inline void LdapCompressWhitespace(nsCString& aString) +{ + // This code is frozen linkage specific + aString.Trim(" \f\n\r\t\v"); + + char *start, *end; + aString.BeginWriting(&start, &end); + + for (char *cur = start; cur < end; ++cur) { + if (!IS_SPACE(*cur)) + continue; + + *cur = ' '; + + if (!IS_SPACE(*(cur + 1))) + continue; + + // Loop through the white space + char *wend = cur + 2; + while (IS_SPACE(*wend)) + ++wend; + + uint32_t wlen = wend - cur - 1; + + // fix "end" + end -= wlen; + + // move everything forwards a bit + for (char *m = cur + 1; m < end; ++m) { + *m = *(m + wlen); + } + } + + // Set the new length. + aString.SetLength(end - start); +} + +/* + * Function copied from nsReadableUtils. + * Migrating to frozen linkage is the only change done + */ +inline +bool IsUTF8(const nsACString& aString) +{ + const char *done_reading = aString.EndReading(); + + int32_t state = 0; + bool overlong = false; + bool surrogate = false; + bool nonchar = false; + uint16_t olupper = 0; // overlong byte upper bound. + uint16_t slower = 0; // surrogate byte lower bound. + + const char *ptr = aString.BeginReading(); + + while (ptr < done_reading) { + uint8_t c; + + if (0 == state) { + + c = *ptr++; + + if ((c & 0x80) == 0x00) + continue; + + if ( c <= 0xC1 ) // [80-BF] where not expected, [C0-C1] for overlong. + return false; + else if ((c & 0xE0) == 0xC0) + state = 1; + else if ((c & 0xF0) == 0xE0) { + state = 2; + if ( c == 0xE0 ) { // to exclude E0[80-9F][80-BF] + overlong = true; + olupper = 0x9F; + } else if ( c == 0xED ) { // ED[A0-BF][80-BF] : surrogate codepoint + surrogate = true; + slower = 0xA0; + } else if ( c == 0xEF ) // EF BF [BE-BF] : non-character + nonchar = true; + } else if ( c <= 0xF4 ) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090) + state = 3; + nonchar = true; + if ( c == 0xF0 ) { // to exclude F0[80-8F][80-BF]{2} + overlong = true; + olupper = 0x8F; + } + else if ( c == 0xF4 ) { // to exclude F4[90-BF][80-BF] + // actually not surrogates but codepoints beyond 0x10FFFF + surrogate = true; + slower = 0x90; + } + } else + return false; // Not UTF-8 string + } + + while (ptr < done_reading && state) { + c = *ptr++; + --state; + + // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF] + if ( nonchar && ( !state && c < 0xBE || + state == 1 && c != 0xBF || + state == 2 && 0x0F != (0x0F & c) )) + nonchar = false; + + if ((c & 0xC0) != 0x80 || overlong && c <= olupper || + surrogate && slower <= c || nonchar && !state ) + return false; // Not UTF-8 string + overlong = surrogate = false; + } + } + return !state; // state != 0 at the end indicates an invalid UTF-8 seq. +} + +#define kNotFound -1 + +#define nsCaseInsensitiveCStringComparator() \ + CaseInsensitiveCompare +#define nsCaseInsensitiveStringComparator() \ + CaseInsensitiveCompare + +#endif // MOZILLA_INTERNAL_API diff --git a/mailnews/addrbook/content/abAddressBookNameDialog.js b/mailnews/addrbook/content/abAddressBookNameDialog.js new file mode 100644 index 0000000000..a62659cc38 --- /dev/null +++ b/mailnews/addrbook/content/abAddressBookNameDialog.js @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource:///modules/mailServices.js"); + +var gOkButton; +var gNameInput; +var gDirectory = null; + +var kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab"; +var kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab"; +var kAllDirectoryRoot = "moz-abdirectory://"; +var kPABDirectory = 2; // defined in nsDirPrefs.h + +function abNameOnLoad() +{ + // Get the document elements. + gOkButton = document.documentElement.getButton('accept'); + gNameInput = document.getElementById('name'); + + // look in arguments[0] for parameters to see if we have a directory or not + if ("arguments" in window && window.arguments[0] && + "selectedDirectory" in window.arguments[0]) { + gDirectory = window.arguments[0].selectedDirectory; + gNameInput.value = gDirectory.dirName; + } + + // Work out the window title (if we have a directory specified, then it's a + // rename). + var bundle = document.getElementById("bundle_addressBook"); + + if (gDirectory) { + let oldListName = gDirectory.dirName; + document.title = bundle.getFormattedString("addressBookTitleEdit", [oldListName]); + } else { + document.title = bundle.getString("addressBookTitleNew"); + } + + if (gDirectory && + (gDirectory.URI == kCollectedAddressbookURI || + gDirectory.URI == kPersonalAddressbookURI || + gDirectory.URI == kAllDirectoryRoot + "?")) { + // Address book name is not editable, therefore disable the field and + // only have an ok button that doesn't do anything. + gNameInput.readOnly = true; + document.documentElement.buttons = "accept"; + document.documentElement.removeAttribute("ondialogaccept"); + } else { + gNameInput.focus(); + abNameDoOkEnabling(); + } +} + +function abNameOKButton() +{ + var newName = gNameInput.value.trim(); + + // Either create a new directory or update an existing one depending on what + // we were given when we started. + if (gDirectory) + gDirectory.dirName = newName; + else + MailServices.ab.newAddressBook(newName, "", kPABDirectory); + + return true; +} + +function abNameDoOkEnabling() +{ + gOkButton.disabled = gNameInput.value.trim() == ""; +} diff --git a/mailnews/addrbook/content/abAddressBookNameDialog.xul b/mailnews/addrbook/content/abAddressBookNameDialog.xul new file mode 100644 index 0000000000..f707cd5979 --- /dev/null +++ b/mailnews/addrbook/content/abAddressBookNameDialog.xul @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &folderRebuildSummaryFile.explanation; + + diff --git a/mailnews/base/content/folderWidgets.xml b/mailnews/base/content/folderWidgets.xml new file mode 100644 index 0000000000..605f4230ce --- /dev/null +++ b/mailnews/base/content/folderWidgets.xml @@ -0,0 +1,829 @@ + + + + + + + + + + + + + + + null + + + null + + + + + + + 15 + + + true + + + + + + + false + + + + acct.incomingServer.rootFolder); + } else { + // If we do have a parent folder, then we just build based on those + // subFolders for that parent. + folders = this.toArray(this.fixIterator(this._parentFolder.subFolders, + Ci.nsIMsgFolder)); + } + + this._build(folders); + + // Lastly, we add a listener to get notified of changes in the folder + // structure. + this.MailServices.mailSession.AddFolderListener(this._listener, + Ci.nsIFolderListener.all); + + this._initialized = true; + ]]> + + + + + + 0) { + folders = folders.filter(function(aFolder) { + return !(excludeServers.indexOf(aFolder.server.key) != -1); }); + } + + /* This code block will do the following: Add a menu item that refers + back to the parent folder when there is a showFileHereLabel + attribute or no mode attribute. However the code won't add such a + menu item if one of the following conditions is met: + (*) There is no parent folder + (*) Folder is server and showAccountsFileHere is explicitly false + (*) Current folder has a mode, the parent folder can be selected, + no messages can be filed into the parent folder (e.g. when the + parent folder is a news group or news server) and the folder + mode is not equal to newFolder + + The menu item will have the value of the fileHereLabel attribute as + label or if the attribute does not exist the name of the parent + folder instead. + */ + let parent = this._parentFolder; + if (parent && (this.getAttribute("showFileHereLabel") == "true" || !mode)) { + let showAccountsFileHere = this.getAttribute("showAccountsFileHere"); + if ((!parent.isServer || showAccountsFileHere != "false") && + (!mode || mode == "newFolder" || parent.noSelect || + parent.canFileMessages || showAccountsFileHere == "true")) { + var menuitem = document.createElement("menuitem"); + menuitem._folder = this._parentFolder; + menuitem.setAttribute("generated", "true"); + if (this.hasAttribute("fileHereLabel")) { + menuitem.setAttribute("label", this.getAttribute("fileHereLabel")); + menuitem.setAttribute("accesskey", this.getAttribute("fileHereAccessKey")); + } else { + menuitem.setAttribute("label", this._parentFolder.prettyName); + menuitem.setAttribute("class", "folderMenuItem menuitem-iconic"); + this._setCssSelectors(this._parentFolder, menuitem); + } + // Eww. have to support some legacy code here... + menuitem.setAttribute("id", this._parentFolder.URI); + this.appendChild(menuitem); + + if (this._parentFolder.noSelect) + menuitem.setAttribute("disabled", "true"); + + var sep= document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + this.appendChild(sep); + } + } + + let globalInboxFolder = null; + // See if this is the toplevel menu (usually with accounts). + if (!this._parentFolder) { + // Some menus want a "Recent" option, but that should only be on our + // top-level menu + if (this.getAttribute("showRecent") == "true") + this._buildRecentMenu(); + // If we are showing the accounts for deferring, move Local Folders to the top. + if (mode == "deferred") { + globalInboxFolder = this.MailServices.accounts.localFoldersServer + .rootFolder; + let localFoldersIndex = folders.indexOf(globalInboxFolder); + if (localFoldersIndex != -1) { + folders.splice(localFoldersIndex, 1); + folders.unshift(globalInboxFolder); + } + } + // If we're the root of the folder hierarchy, then we actually don't + // want to sort the folders, but rather the accounts to which the + // folders belong. Since that sorting was already done, we don't need + // to do anything for that case here. + } else { + // Sorts the list of folders. We give first priority to the sortKey + // property if it is available, otherwise a case-insensitive + // comparison of names. + folders = folders.sort(function nameCompare(a, b) { + return a.compareSortKeys(b); + }); + } + + /* In some cases, the user wants to have a list of subfolders for only + * some account types (or maybe all of them). So we use this to + * determine what the user wanted. + */ + var shouldExpand; + var labels = null; + if (this.getAttribute("expandFolders") == "true" || + !this.hasAttribute("expandFolders")) { + shouldExpand = function (e) { return true; }; + } else if (this.getAttribute("expandFolders") == "false") { + shouldExpand = function (e) { return false; }; + } else { + /* We want a subfolder list for only some servers. We also may need + * to create headers to select the servers. If so, then headlabels + * is a comma-delimited list of labels corresponding to the server + * types specified in expandFolders. + */ + var types = this.getAttribute("expandFolders").split(/ *, */); + // Set the labels. labels[type] = label + if (this.hasAttribute("headlabels")) { + var labelNames = this.getAttribute("headlabels").split(/ *, */); + labels = {}; + // If the length isn't equal, don't give them any of the labels, + // since any combination will probably be wrong. + if (labelNames.length == types.length) { + for (var index in types) + labels[types[index]] = labelNames[index]; + } + } + shouldExpand = function (e) { return types.indexOf(e) != -1; }; + } + + // We need to call this, or hasSubFolders will always return false. + // Remove this workaround when Bug 502900 is fixed. + this.MailUtils.discoverFolders(); + this._serversOnly = true; + + for (let folder of folders) { + let node; + if (!folder.isServer) + this._serversOnly = false; + + // If we're going to add subFolders, we need to make menus, not + // menuitems. + if (!folder.hasSubFolders || !shouldExpand(folder.server.type)) { + node = document.createElement("menuitem"); + // Grumble, grumble, legacy code support + node.setAttribute("id", folder.URI); + node.setAttribute("class", "folderMenuItem menuitem-iconic"); + node.setAttribute("generated", "true"); + this.appendChild(node); + } else { + this._serversOnly = false; + //xxx this is slightly problematic in that we haven't confirmed + // whether any of the subfolders will pass the filter + node = document.createElement("menu"); + node.setAttribute("class", "folderMenuItem menu-iconic"); + node.setAttribute("generated", "true"); + this.appendChild(node); + + // Create the submenu + // (We must use cloneNode here because on OS X the native menu + // functionality and very sad limitations of XBL1 cause the bindings + // to never get created for popup if we create a new element. We + // perform a shallow clone to avoid picking up any of our children.) + var popup = this.cloneNode(false); + popup._parentFolder = folder; + popup.setAttribute("class", this.getAttribute("class")); + popup.setAttribute("type", this.getAttribute("type")); + if (this.hasAttribute("fileHereLabel")) + popup.setAttribute("fileHereLabel", + this.getAttribute("fileHereLabel")); + popup.setAttribute("showFileHereLabel", + this.getAttribute("showFileHereLabel")); + popup.setAttribute("oncommand", + this.getAttribute("oncommand")); + popup.setAttribute("mode", + this.getAttribute("mode")); + if (this.hasAttribute("disableServers")) + popup.setAttribute("disableServers", + this.getAttribute("disableServers")); + if (this.hasAttribute("position")) + popup.setAttribute("position", + this.getAttribute("position")); + + // If there are labels, add the labels now + if (labels) { + var serverNode = document.createElement("menuitem"); + serverNode.setAttribute("label", labels[folder.server.type]); + serverNode._folder = folder; + serverNode.setAttribute("generated", "true"); + popup.appendChild(serverNode); + var sep = document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + popup.appendChild(sep); + } + + popup.setAttribute("generated", "true"); + node.appendChild(popup); + } + + if (disableServers.indexOf(folder.server.key) != -1) + node.setAttribute("disabled", "true"); + + node._folder = folder; + let label = ""; + if (mode == "deferred" && folder.isServer && + folder.server.rootFolder == globalInboxFolder) { + label = this._stringBundle.get("globalInbox", [folder.prettyName]); + } else { + label = folder.prettyName; + } + node.setAttribute("label", label); + this._setCssSelectors(folder, node); + } + ]]> + + + + + f.canFileMessages); + + let recentFolders = this.getMostRecentFolders(allFolders, + this._MAXRECENT, + "MRMTime"); + + // Cache the pretty names so that they do not need to be fetched + // _MAXRECENT^2 times later. + recentFolders = recentFolders.map( + function (f) { return { folder: f, name: f.prettyName } }); + + // Because we're scanning across multiple accounts, we can end up with + // several folders with the same name. Find those dupes. + let dupeNames = new Set(); + for (let i = 0; i < recentFolders.length; i++) { + for (let j = i + 1; j < recentFolders.length; j++) { + if (recentFolders[i].name == recentFolders[j].name) + dupeNames.add(recentFolders[i].name); + } + } + + for (let folderItem of recentFolders) { + // If this folder name appears multiple times in the recent list, + // append the server name to disambiguate. + // TODO: + // - maybe this could use verboseFolderFormat from messenger.properties + // instead of hardcoded " - ". + // - disambiguate folders with same name in same account + // (in different subtrees). + let label = folderItem.name; + if (dupeNames.has(label)) + label += " - " + folderItem.folder.server.prettyName; + + folderItem.label = label; + } + + // Make sure the entries are sorted alphabetically. + recentFolders.sort((a, b) => this.folderNameCompare(a.label, b.label)); + + // Now create the Recent folder and its children + var menu = document.createElement("menu"); + menu.setAttribute("label", this.getAttribute("recentLabel")); + menu.setAttribute("accesskey", this.getAttribute("recentAccessKey")); + var popup = document.createElement("menupopup"); + popup.setAttribute("class", this.getAttribute("class")); + popup.setAttribute("generated", "true"); + menu.appendChild(popup); + + // Create entries for each of the recent folders. + for (let folderItem of recentFolders) { + let node = document.createElement("menuitem"); + + node.setAttribute("label", folderItem.label); + node._folder = folderItem.folder; + + node.setAttribute("class", "folderMenuItem menuitem-iconic"); + this._setCssSelectors(folderItem.folder, node); + node.setAttribute("generated", "true"); + popup.appendChild(node); + } + menu.setAttribute("generated", "true"); + this.appendChild(menu); + if (!recentFolders.length) + menu.setAttribute("disabled", "true"); + + var sep = document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + this.appendChild(sep); + ]]> + + + + + + + + + + + null + + + + + + + + + + + + + + = 0; i--) { + let child = this.childNodes[i]; + if (child.getAttribute("generated") != "true") + continue; + if ("_teardown" in child) + child._teardown(); + child.remove(); + } + + this._removeListener(); + + this._initialized = false; + ]]> + + + + + + + this._ensureInitialized(); + + + + diff --git a/mailnews/base/content/jsTreeView.js b/mailnews/base/content/jsTreeView.js new file mode 100644 index 0000000000..abec7df4ac --- /dev/null +++ b/mailnews/base/content/jsTreeView.js @@ -0,0 +1,235 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This file contains a prototype object designed to make the implementation of + * nsITreeViews in javascript simpler. This object requires that consumers + * override the _rebuild function. This function must set the _rowMap object to + * an array of objects fitting the following interface: + * + * readonly attribute string id - a unique identifier for the row/object + * readonly attribute integer level - the hierarchy level of the row + * attribute boolean open - whether or not this item's children are exposed + * string getText(aColName) - return the text to display for this row in the + * specified column + * string getProperties() - return the css-selectors + * attribute array children - return an array of child-objects also meeting this + * interface + */ + +function PROTO_TREE_VIEW() { + this._tree = null; + this._rowMap = []; + this._persistOpenMap = []; +} + +PROTO_TREE_VIEW.prototype = { + get rowCount() { + return this._rowMap.length; + }, + + /** + * CSS files will cue off of these. Note that we reach into the rowMap's + * items so that custom data-displays can define their own properties + */ + getCellProperties: function jstv_getCellProperties(aRow, aCol) { + return this._rowMap[aRow].getProperties(aCol); + }, + + /** + * The actual text to display in the tree + */ + getCellText: function jstv_getCellText(aRow, aCol) { + return this._rowMap[aRow].getText(aCol.id); + }, + + /** + * The jstv items take care of assigning this when building children lists + */ + getLevel: function jstv_getLevel(aIndex) { + return this._rowMap[aIndex].level; + }, + + /** + * This is easy since the jstv items assigned the _parent property when making + * the child lists + */ + getParentIndex: function jstv_getParentIndex(aIndex) { + return this._rowMap.indexOf(this._rowMap[aIndex]._parent); + }, + + /** + * This is duplicative for our normal jstv views, but custom data-displays may + * want to do something special here + */ + getRowProperties: function jstv_getRowProperties(aRow) { + return this._rowMap[aRow].getProperties(); + }, + + /** + * If an item in our list has the same level and parent as us, it's a sibling + */ + hasNextSibling: function jstv_hasNextSibling(aIndex, aNextIndex) { + let targetLevel = this._rowMap[aIndex].level; + for (let i = aNextIndex + 1; i < this._rowMap.length; i++) { + if (this._rowMap[i].level == targetLevel) + return true; + if (this._rowMap[i].level < targetLevel) + return false; + } + return false; + }, + + /** + * If we have a child-list with at least one element, we are a container. + */ + isContainer: function jstv_isContainer(aIndex) { + return this._rowMap[aIndex].children.length > 0; + }, + + isContainerEmpty: function jstv_isContainerEmpty(aIndex) { + // If the container has no children, the container is empty. + return !this._rowMap[aIndex].children.length; + }, + + /** + * Just look at the jstv item here + */ + isContainerOpen: function jstv_isContainerOpen(aIndex) { + return this._rowMap[aIndex].open; + }, + + isEditable: function jstv_isEditable(aRow, aCol) { + // We don't support editing rows in the tree yet. + return false; + }, + + isSeparator: function jstv_isSeparator(aIndex) { + // There are no separators in our trees + return false; + }, + + isSorted: function jstv_isSorted() { + // We do our own customized sorting + return false; + }, + + setTree: function jstv_setTree(aTree) { + this._tree = aTree; + }, + + recursivelyAddToMap: function jstv_recursivelyAddToMap(aChild, aNewIndex) { + // When we add sub-children, we're going to need to increase our index + // for the next add item at our own level. + let currentCount = this._rowMap.length; + if (aChild.children.length && aChild.open) { + for (let [i, child] in Iterator(this._rowMap[aNewIndex].children)) { + let index = aNewIndex + i + 1; + this._rowMap.splice(index, 0, child); + aNewIndex += this.recursivelyAddToMap(child, index); + } + } + return this._rowMap.length - currentCount; + }, + + /** + * Opens or closes a container with children. The logic here is a bit hairy, so + * be very careful about changing anything. + */ + toggleOpenState: function jstv_toggleOpenState(aIndex) { + + // Ok, this is a bit tricky. + this._rowMap[aIndex]._open = !this._rowMap[aIndex].open; + + if (!this._rowMap[aIndex].open) { + // We're closing the current container. Remove the children + + // Note that we can't simply splice out children.length, because some of + // them might have children too. Find out how many items we're actually + // going to splice + let level = this._rowMap[aIndex].level; + let row = aIndex + 1; + while (row < this._rowMap.length && this._rowMap[row].level > level) { + row++; + } + let count = row - aIndex - 1; + this._rowMap.splice(aIndex + 1, count); + + // Remove us from the persist map + let index = this._persistOpenMap.indexOf(this._rowMap[aIndex].id); + if (index != -1) + this._persistOpenMap.splice(index, 1); + + // Notify the tree of changes + if (this._tree) { + this._tree.rowCountChanged(aIndex + 1, -count); + } + } else { + // We're opening the container. Add the children to our map + + // Note that these children may have been open when we were last closed, + // and if they are, we also have to add those grandchildren to the map + let oldCount = this._rowMap.length; + this.recursivelyAddToMap(this._rowMap[aIndex], aIndex); + + // Add this container to the persist map + let id = this._rowMap[aIndex].id; + if (this._persistOpenMap.indexOf(id) == -1) + this._persistOpenMap.push(id); + + // Notify the tree of changes + if (this._tree) + this._tree.rowCountChanged(aIndex + 1, this._rowMap.length - oldCount); + } + + // Invalidate the toggled row, so that the open/closed marker changes + if (this._tree) + this._tree.invalidateRow(aIndex); + }, + + // We don't implement any of these at the moment + canDrop: function jstv_canDrop(aIndex, aOrientation) {}, + drop: function jstv_drop(aRow, aOrientation) {}, + performAction: function jstv_performAction(aAction) {}, + performActionOnCell: function jstv_performActionOnCell(aAction, aRow, aCol) {}, + performActionOnRow: function jstv_performActionOnRow(aAction, aRow) {}, + selectionChanged: function jstv_selectionChanged() {}, + setCellText: function jstv_setCellText(aRow, aCol, aValue) {}, + setCellValue: function jstv_setCellValue(aRow, aCol, aValue) {}, + getCellValue: function jstv_getCellValue(aRow, aCol) {}, + getColumnProperties: function jstv_getColumnProperties(aCol) { return ""; }, + getImageSrc: function jstv_getImageSrc(aRow, aCol) {}, + getProgressMode: function jstv_getProgressMode(aRow, aCol) {}, + cycleCell: function jstv_cycleCell(aRow, aCol) {}, + cycleHeader: function jstv_cycleHeader(aCol) {}, + + _tree: null, + + /** + * An array of jstv items, where each item corresponds to a row in the tree + */ + _rowMap: null, + + /** + * This is a javascript map of which containers we had open, so that we can + * persist their state over-time. It is designed to be used as a JSON object. + */ + _persistOpenMap: null, + + _restoreOpenStates: function jstv__restoreOpenStates() { + // Note that as we iterate through here, .length may grow + for (let i = 0; i < this._rowMap.length; i++) { + if (this._persistOpenMap.indexOf(this._rowMap[i].id) != -1) + this.toggleOpenState(i); + } + }, + + QueryInterface: function QueryInterface(aIID) { + if (aIID.equals(Components.interfaces.nsITreeView) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + + throw Components.results.NS_ERROR_NO_INTERFACE; + } +}; diff --git a/mailnews/base/content/junkCommands.js b/mailnews/base/content/junkCommands.js new file mode 100644 index 0000000000..6332d193ff --- /dev/null +++ b/mailnews/base/content/junkCommands.js @@ -0,0 +1,456 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + /* functions use for junk processing commands + * + * TODO: These functions make the false assumption that a view only contains + * a single folder. This is not true for XF saved searches. + * + * globals prerequisites used: + * + * window.MsgStatusFeedback + * + * One of: + * GetSelectedIndices(view) (in suite) + * gFolderDisplay (in mail) + * + * messenger + * gMessengerBundle + * gDBView + * either gMsgFolderSelected or gFolderDisplay + * MsgJunkMailInfo(aCheckFirstUse) + * SetNextMessageAfterDelete() + * pref + * msgWindow + */ + +Components.utils.import("resource:///modules/mailServices.js"); +Components.utils.import("resource:///modules/MailUtils.js"); +Components.utils.import("resource://gre/modules/Services.jsm"); + +/* + * determineActionsForJunkMsgs + * + * Determines the actions that should be carried out on the messages + * that are being marked as junk + * + * @param aFolder + * the folder with messages being marked as junk + * + * @return an object with two properties: 'markRead' (boolean) indicating + * whether the messages should be marked as read, and 'junkTargetFolder' + * (nsIMsgFolder) specifying where the messages should be moved, or + * null if they should not be moved. + */ +function determineActionsForJunkMsgs(aFolder) +{ + var actions = { markRead: false, junkTargetFolder: null }; + var spamSettings = aFolder.server.spamSettings; + + // note we will do moves/marking as read even if the spam + // feature is disabled, since the user has asked to use it + // despite the disabling + + actions.markRead = spamSettings.markAsReadOnSpam; + actions.junkTargetFolder = null; + + // move only when the corresponding setting is activated + // and the currently viewed folder is not the junk folder. + if (spamSettings.moveOnSpam && + !(aFolder.flags & Components.interfaces.nsMsgFolderFlags.Junk)) + { + var spamFolderURI = spamSettings.spamFolderURI; + if (!spamFolderURI) + { + // XXX TODO + // we should use nsIPromptService to inform the user of the problem, + // e.g. when the junk folder was accidentally deleted. + dump('determineActionsForJunkMsgs: no spam folder found, not moving.'); + } + else + actions.junkTargetFolder = MailUtils.getFolderForURI(spamFolderURI); + } + + return actions; +} + +/** + * performActionsOnJunkMsgs + * + * Performs required operations on a list of newly-classified junk messages + * + * @param aFolder + * the folder with messages being marked as junk + * + * @param aJunkMsgHdrs + * nsIArray containing headers (nsIMsgDBHdr) of new junk messages + * + * @param aGoodMsgHdrs + * nsIArray containing headers (nsIMsgDBHdr) of new good messages + */ + function performActionsOnJunkMsgs(aFolder, aJunkMsgHdrs, aGoodMsgHdrs) +{ + if (aFolder instanceof Components.interfaces.nsIMsgImapMailFolder) // need to update IMAP custom flags + { + if (aJunkMsgHdrs.length) + { + var junkMsgKeys = new Array(); + for (var i = 0; i < aJunkMsgHdrs.length; i++) + junkMsgKeys[i] = aJunkMsgHdrs.queryElementAt(i, Components.interfaces.nsIMsgDBHdr).messageKey; + aFolder.storeCustomKeywords(null, "Junk", "NonJunk", junkMsgKeys, junkMsgKeys.length); + } + + if (aGoodMsgHdrs.length) + { + var goodMsgKeys = new Array(); + for (var i = 0; i < aGoodMsgHdrs.length; i++) + goodMsgKeys[i] = aGoodMsgHdrs.queryElementAt(i, Components.interfaces.nsIMsgDBHdr).messageKey; + aFolder.storeCustomKeywords(null, "NonJunk", "Junk", goodMsgKeys, goodMsgKeys.length); + } + } + + if (aJunkMsgHdrs.length) + { + var actionParams = determineActionsForJunkMsgs(aFolder); + if (actionParams.markRead) + aFolder.markMessagesRead(aJunkMsgHdrs, true); + + if (actionParams.junkTargetFolder) + MailServices.copy + .CopyMessages(aFolder, aJunkMsgHdrs, actionParams.junkTargetFolder, + true /* isMove */, null, msgWindow, true /* allow undo */); + } +} + +/** + * MessageClassifier + * + * Helper object storing the list of pending messages to process, + * and implementing junk processing callback + * + * @param aFolder + * the folder with messages to be analyzed for junk + * @param aTotalMessages + * Number of messages to process, used for progress report only + */ + +function MessageClassifier(aFolder, aTotalMessages) +{ + this.mFolder = aFolder; + this.mJunkMsgHdrs = Components.classes["@mozilla.org/array;1"] + .createInstance(Components.interfaces.nsIMutableArray); + this.mGoodMsgHdrs = Components.classes["@mozilla.org/array;1"] + .createInstance(Components.interfaces.nsIMutableArray); + this.mMessages = new Object(); + this.mMessageQueue = new Array(); + this.mTotalMessages = aTotalMessages; + this.mProcessedMessages = 0; + this.firstMessage = true; + this.lastStatusTime = Date.now(); +} + +MessageClassifier.prototype = +{ + /** + * analyzeMessage + * + * Starts the message classification process for a message. If the message + * sender's address is whitelisted, the message is skipped. + * + * @param aMsgHdr + * The header (nsIMsgDBHdr) of the message to classify. + * @param aSpamSettings + * nsISpamSettings object with information about whitelists + */ + analyzeMessage: function(aMsgHdr, aSpamSettings) + { + var junkscoreorigin = aMsgHdr.getStringProperty("junkscoreorigin"); + if (junkscoreorigin == "user") // don't override user-set junk status + return; + + // check whitelisting + if (aSpamSettings.checkWhiteList(aMsgHdr)) + { + // message is ham from whitelist + var db = aMsgHdr.folder.msgDatabase; + db.setStringProperty(aMsgHdr.messageKey, "junkscore", + Components.interfaces.nsIJunkMailPlugin.IS_HAM_SCORE); + db.setStringProperty(aMsgHdr.messageKey, "junkscoreorigin", "whitelist"); + this.mGoodMsgHdrs.appendElement(aMsgHdr, false); + return; + } + + var messageURI = aMsgHdr.folder.generateMessageURI(aMsgHdr.messageKey) + "?fetchCompleteMessage=true"; + this.mMessages[messageURI] = aMsgHdr; + if (this.firstMessage) + { + this.firstMessage = false; + MailServices.junk.classifyMessage(messageURI, msgWindow, this); + } + else + this.mMessageQueue.push(messageURI); + }, + + /* + * nsIJunkMailClassificationListener implementation + * onMessageClassified + * + * Callback function from nsIJunkMailPlugin with classification results + * + * @param aClassifiedMsgURI + * URI of classified message + * @param aClassification + * Junk classification (0: UNCLASSIFIED, 1: GOOD, 2: JUNK) + * @param aJunkPercent + * 0 - 100 indicator of junk likelihood, with 100 meaning probably junk + */ + onMessageClassified: function(aClassifiedMsgURI, aClassification, aJunkPercent) + { + if (!aClassifiedMsgURI) + return; // ignore end of batch + var nsIJunkMailPlugin = Components.interfaces.nsIJunkMailPlugin; + var score = (aClassification == nsIJunkMailPlugin.JUNK) ? + nsIJunkMailPlugin.IS_SPAM_SCORE : nsIJunkMailPlugin.IS_HAM_SCORE; + const statusDisplayInterval = 1000; // milliseconds between status updates + + // set these props via the db (instead of the message header + // directly) so that the nsMsgDBView knows to update the UI + // + var msgHdr = this.mMessages[aClassifiedMsgURI]; + var db = msgHdr.folder.msgDatabase; + db.setStringProperty(msgHdr.messageKey, "junkscore", score); + db.setStringProperty(msgHdr.messageKey, "junkscoreorigin", "plugin"); + db.setStringProperty(msgHdr.messageKey, "junkpercent", aJunkPercent); + + if (aClassification == nsIJunkMailPlugin.JUNK) + this.mJunkMsgHdrs.appendElement(msgHdr, false); + else if (aClassification == nsIJunkMailPlugin.GOOD) + this.mGoodMsgHdrs.appendElement(msgHdr, false); + + var nextMsgURI = this.mMessageQueue.shift(); + if (nextMsgURI) + { + ++this.mProcessedMessages; + if (Date.now() > this.lastStatusTime + statusDisplayInterval) + { + this.lastStatusTime = Date.now(); + var percentDone = 0; + if (this.mTotalMessages) + percentDone = Math.round(this.mProcessedMessages * 100 / this.mTotalMessages); + var percentStr = percentDone + "%"; + window.MsgStatusFeedback.showStatusString( + document.getElementById("bundle_messenger") + .getFormattedString("junkAnalysisPercentComplete", + [percentStr])); + } + + MailServices.junk.classifyMessage(nextMsgURI, msgWindow, this); + } + else + { + window.MsgStatusFeedback.showStatusString( + document.getElementById("bundle_messenger") + .getString("processingJunkMessages")); + performActionsOnJunkMsgs(this.mFolder, this.mJunkMsgHdrs, this.mGoodMsgHdrs); + window.MsgStatusFeedback.showStatusString(""); + } + } +} + +/* + * filterFolderForJunk + * + * Filter all messages in the current folder for junk + */ +function filterFolderForJunk() { processFolderForJunk(true); } + +/* + * analyzeMessagesForJunk + * + * Filter selected messages in the current folder for junk + */ +function analyzeMessagesForJunk() { processFolderForJunk(false); } + +/* + * processFolderForJunk + * + * Filter messages in the current folder for junk + * + * @param aAll: true to filter all messages, else filter selection + */ +function processFolderForJunk(aAll) +{ + MsgJunkMailInfo(true); + + if (aAll) + { + // need to expand all threads, so we analyze everything + gDBView.doCommand(nsMsgViewCommandType.expandAll); + var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView); + var count = treeView.rowCount; + if (!count) + return; + } + else + { + // suite uses GetSelectedIndices, mail uses gFolderDisplay.selectedMessages + var indices = typeof GetSelectedIndices != "undefined" ? + GetSelectedIndices(gDBView) : + gFolderDisplay.selectedIndices; + if (!indices || !indices.length) + return; + } + var totalMessages = aAll ? count : indices.length; + + // retrieve server and its spam settings via the header of an arbitrary message + for (var i = 0; i < totalMessages; i++) + { + var index = aAll ? i : indices[i]; + try + { + var tmpMsgURI = gDBView.getURIForViewIndex(index); + break; + } + catch (e) + { + // dummy headers will fail, so look for another + continue; + } + } + if (!tmpMsgURI) + return; + + var tmpMsgHdr = messenger.messageServiceFromURI(tmpMsgURI).messageURIToMsgHdr(tmpMsgURI); + var spamSettings = tmpMsgHdr.folder.server.spamSettings; + + // create a classifier instance to classify messages in the folder. + var msgClassifier = new MessageClassifier(tmpMsgHdr.folder, totalMessages); + + for ( i = 0; i < totalMessages; i++) + { + var index = aAll ? i : indices[i]; + try + { + var msgURI = gDBView.getURIForViewIndex(index); + var msgHdr = messenger.messageServiceFromURI(msgURI).messageURIToMsgHdr(msgURI); + msgClassifier.analyzeMessage(msgHdr, spamSettings); + } + catch (ex) + { + // blow off errors here - dummy headers will fail + var msgURI = null; + } + } + if (msgClassifier.firstMessage) // the async plugin was not used, maybe all whitelisted? + performActionsOnJunkMsgs(msgClassifier.mFolder, + msgClassifier.mJunkMsgHdrs, + msgClassifier.mGoodMsgHdrs); +} + +function JunkSelectedMessages(setAsJunk) +{ + MsgJunkMailInfo(true); + + // When the user explicitly marks a message as junk, we can mark it as read, + // too. This is independent of the "markAsReadOnSpam" pref, which applies + // only to automatically-classified messages. + // Note that this behaviour should match the one in the back end for marking + // as junk via clicking the 'junk' column. + + if (setAsJunk && Services.prefs.getBoolPref("mailnews.ui.junk.manualMarkAsJunkMarksRead")) + MarkSelectedMessagesRead(true); + + gDBView.doCommand(setAsJunk ? nsMsgViewCommandType.junk + : nsMsgViewCommandType.unjunk); +} + +/** + * Delete junk messages in the current folder. This provides the guarantee that + * the method will be synchronous if no messages are deleted. + * + * @returns The number of messages deleted. + */ +function deleteJunkInFolder() +{ + MsgJunkMailInfo(true); + + // use direct folder commands if possible so we don't mess with the selection + let selectedFolder = gFolderDisplay.displayedFolder; + if ( !(selectedFolder.flags & Components.interfaces.nsMsgFolderFlags.Virtual) ) + { + var junkMsgHdrs = Components.classes["@mozilla.org/array;1"] + .createInstance(Components.interfaces.nsIMutableArray); + var enumerator = gDBView.msgFolder.messages; + while (enumerator.hasMoreElements()) + { + var msgHdr = enumerator.getNext().QueryInterface(Components.interfaces.nsIMsgDBHdr); + var junkScore = msgHdr.getStringProperty("junkscore"); + if (junkScore == Components.interfaces.nsIJunkMailPlugin.IS_SPAM_SCORE) + junkMsgHdrs.appendElement(msgHdr, false); + } + + if (junkMsgHdrs.length) + gDBView.msgFolder.deleteMessages(junkMsgHdrs, msgWindow, false, false, null, true); + return junkMsgHdrs.length; + } + + // Folder is virtual, let the view do the work (but we lose selection) + + // need to expand all threads, so we find everything + gDBView.doCommand(nsMsgViewCommandType.expandAll); + + var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView); + var count = treeView.rowCount; + if (!count) + return 0; + + var treeSelection = treeView.selection; + + var clearedSelection = false; + + // select the junk messages + var messageUri; + let numMessagesDeleted = 0; + for (var i = 0; i < count; ++i) + { + try { + messageUri = gDBView.getURIForViewIndex(i); + } + catch (ex) {continue;} // blow off errors for dummy rows + var msgHdr = messenger.messageServiceFromURI(messageUri).messageURIToMsgHdr(messageUri); + var junkScore = msgHdr.getStringProperty("junkscore"); + var isJunk = (junkScore == Components.interfaces.nsIJunkMailPlugin.IS_SPAM_SCORE); + // if the message is junk, select it. + if (isJunk) + { + // only do this once + if (!clearedSelection) + { + // clear the current selection + // since we will be deleting all selected messages + treeSelection.clearSelection(); + clearedSelection = true; + treeSelection.selectEventsSuppressed = true; + } + treeSelection.rangedSelect(i, i, true /* augment */); + numMessagesDeleted++; + } + } + + // if we didn't clear the selection + // there was no junk, so bail. + if (!clearedSelection) + return 0; + + treeSelection.selectEventsSuppressed = false; + // delete the selected messages + // + // We'll leave no selection after the delete + gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None; + gDBView.doCommand(nsMsgViewCommandType.deleteMsg); + treeSelection.clearSelection(); + ClearMessagePane(); + return numMessagesDeleted; +} + diff --git a/mailnews/base/content/junkLog.js b/mailnews/base/content/junkLog.js new file mode 100644 index 0000000000..148f0e5073 --- /dev/null +++ b/mailnews/base/content/junkLog.js @@ -0,0 +1,33 @@ +/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +var gLogView; +var gLogFile; + +function onLoad() +{ + gLogView = document.getElementById("logView"); + gLogView.docShell.allowJavascript = false; // for security, disable JS + + gLogFile = Services.dirsvc.get("ProfD", Components.interfaces.nsIFile); + gLogFile.append("junklog.html"); + + if (gLogFile.exists()) + { + // convert the file to a URL so we can load it. + gLogView.setAttribute("src", Services.io.newFileURI(gLogFile).spec); + } +} + +function clearLog() +{ + if (gLogFile.exists()) + { + gLogFile.remove(false); + gLogView.setAttribute("src", "about:blank"); // we don't have a log file to show + } +} diff --git a/mailnews/base/content/junkLog.xul b/mailnews/base/content/junkLog.xul new file mode 100644 index 0000000000..49e8796c32 --- /dev/null +++ b/mailnews/base/content/junkLog.xul @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mailnews/compose/content/menulistCompactBindings.xml b/mailnews/compose/content/menulistCompactBindings.xml new file mode 100644 index 0000000000..5ca2773473 --- /dev/null +++ b/mailnews/compose/content/menulistCompactBindings.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/mailnews/compose/content/sendProgress.js b/mailnews/compose/content/sendProgress.js new file mode 100644 index 0000000000..8354cf9530 --- /dev/null +++ b/mailnews/compose/content/sendProgress.js @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var nsIMsgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode; + +// dialog is just an array we'll use to store various properties from the dialog document... +var dialog; + +// the msgProgress is a nsIMsgProgress object +var msgProgress = null; + +// random global variables... +var itsASaveOperation = false; +var gSendProgressStringBundle; + +// all progress notifications are done through the nsIWebProgressListener implementation... +var progressListener = { + onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) + { + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START) + { + // Put progress meter in undetermined mode. + dialog.progress.setAttribute("mode", "undetermined"); + } + + if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) + { + // we are done sending/saving the message... + // Indicate completion in status area. + var msg; + if (itsASaveOperation) + msg = gSendProgressStringBundle.getString("messageSaved"); + else + msg = gSendProgressStringBundle.getString("messageSent"); + dialog.status.setAttribute("value", msg); + + // Put progress meter at 100%. + dialog.progress.setAttribute("value", 100); + dialog.progress.setAttribute("mode", "normal"); + var percentMsg = gSendProgressStringBundle.getFormattedString("percentMsg", [100]); + dialog.progressText.setAttribute("value", percentMsg); + + window.close(); + } + }, + + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) + { + // Calculate percentage. + var percent; + if (aMaxTotalProgress > 0) + { + percent = Math.round(aCurTotalProgress / aMaxTotalProgress * 100); + if (percent > 100) + percent = 100; + + dialog.progress.removeAttribute("mode"); + + // Advance progress meter. + dialog.progress.setAttribute("value", percent); + + // Update percentage label on progress meter. + var percentMsg = gSendProgressStringBundle.getFormattedString("percentMsg", [percent]); + dialog.progressText.setAttribute("value", percentMsg); + } + else + { + // Progress meter should be barber-pole in this case. + dialog.progress.setAttribute("mode", "undetermined"); + // Update percentage label on progress meter. + dialog.progressText.setAttribute("value", ""); + } + }, + + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) + { + // we can ignore this notification + }, + + onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) + { + if (aMessage != "") + dialog.status.setAttribute("value", aMessage); + }, + + onSecurityChange: function(aWebProgress, aRequest, state) + { + // we can ignore this notification + }, + + QueryInterface : function(iid) + { + if (iid.equals(Components.interfaces.nsIWebProgressListener) || + iid.equals(Components.interfaces.nsISupportsWeakReference) || + iid.equals(Components.interfaces.nsISupports)) + return this; + + throw Components.results.NS_NOINTERFACE; + } +}; + +function onLoad() +{ + // Set global variables. + let subject = ""; + gSendProgressStringBundle = document.getElementById("sendProgressStringBundle"); + + msgProgress = window.arguments[0]; + if (!msgProgress) + { + Components.utils.reportError("Invalid argument to sendProgress.xul."); + window.close(); + return; + } + + if (window.arguments[1]) + { + let progressParams = window.arguments[1].QueryInterface(Components.interfaces.nsIMsgComposeProgressParams); + if (progressParams) + { + itsASaveOperation = (progressParams.deliveryMode != nsIMsgCompDeliverMode.Now); + subject = progressParams.subject; + } + } + + if (subject) { + let title = itsASaveOperation ? "titleSaveMsgSubject" : "titleSendMsgSubject"; + document.title = gSendProgressStringBundle.getFormattedString(title, [subject]); + } else { + let title = itsASaveOperation ? "titleSaveMsg" : "titleSendMsg"; + document.title = gSendProgressStringBundle.getString(title); + } + + dialog = {}; + dialog.status = document.getElementById("dialog.status"); + dialog.progress = document.getElementById("dialog.progress"); + dialog.progressText = document.getElementById("dialog.progressText"); + + // set our web progress listener on the helper app launcher + msgProgress.registerListener(progressListener); +} + +function onUnload() +{ + if (msgProgress) + { + try + { + msgProgress.unregisterListener(progressListener); + msgProgress = null; + } catch (e) {} + } +} + +// If the user presses cancel, tell the app launcher and close the dialog... +function onCancel() +{ + // Cancel app launcher. + try + { + msgProgress.processCanceledByUser = true; + } catch (e) + { + return true; + } + + // don't Close up dialog by returning false, the backend will close the dialog when everything will be aborted. + return false; +} diff --git a/mailnews/compose/content/sendProgress.xul b/mailnews/compose/content/sendProgress.xul new file mode 100644 index 0000000000..45508387f6 --- /dev/null +++ b/mailnews/compose/content/sendProgress.xul @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/mailnews/extensions/smime/content/am-smimeOverlay.xul b/mailnews/extensions/smime/content/am-smimeOverlay.xul new file mode 100644 index 0000000000..eb76b4b2c1 --- /dev/null +++ b/mailnews/extensions/smime/content/am-smimeOverlay.xul @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + +