/ OSX / libsecurity_cdsa_client / lib / dliterators.h
dliterators.h
  1  /*
  2   * Copyright (c) 2000-2004,2011,2014 Apple Inc. All Rights Reserved.
  3   * 
  4   * The contents of this file constitute Original Code as defined in and are
  5   * subject to the Apple Public Source License Version 1.2 (the 'License').
  6   * You may not use this file except in compliance with the License. Please obtain
  7   * a copy of the License at http://www.apple.com/publicsource and read it before
  8   * using this file.
  9   * 
 10   * This Original Code and all software distributed under the License are
 11   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 12   * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 13   * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14   * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 15   * specific language governing rights and limitations under the License.
 16   */
 17  
 18  
 19  //
 20  // dliterators - DL/MDS table access as C++ iterators
 21  //
 22  // This is currently an almost read-only implementation.
 23  // (You can erase but you can't create or modify.)
 24  //
 25  #ifndef _H_CDSA_CLIENT_DLITERATORS
 26  #define _H_CDSA_CLIENT_DLITERATORS
 27  
 28  #include <security_utilities/threading.h>
 29  #include <security_utilities/globalizer.h>
 30  #include <security_utilities/refcount.h>
 31  #include <security_cdsa_utilities/cssmalloc.h>
 32  #include <security_cdsa_utilities/cssmpods.h>
 33  #include <security_cdsa_utilities/cssmerrors.h>
 34  #include <security_cdsa_utilities/cssmdb.h>
 35  #include <security_cdsa_client/dlquery.h>
 36  
 37  
 38  namespace Security {
 39  namespace CssmClient {
 40  
 41  
 42  //
 43  // An abstract interface to a (partial) DLDb-style object.
 44  // This is a particular (open) database that you can perform CSSM database
 45  // operations on.
 46  //
 47  class DLAccess {
 48  public:
 49  	virtual ~DLAccess();
 50  	
 51  	virtual CSSM_HANDLE dlGetFirst(const CSSM_QUERY &query,
 52  		CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes, CSSM_DATA *data,
 53  		CSSM_DB_UNIQUE_RECORD *&id) = 0;
 54  	virtual bool dlGetNext(CSSM_HANDLE handle,
 55  		CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes, CSSM_DATA *data,
 56  		CSSM_DB_UNIQUE_RECORD *&id) = 0;
 57  	virtual void dlAbortQuery(CSSM_HANDLE handle) = 0;
 58  	virtual void dlFreeUniqueId(CSSM_DB_UNIQUE_RECORD *id) = 0;
 59  	virtual void dlDeleteRecord(CSSM_DB_UNIQUE_RECORD *id) = 0;
 60  	virtual Allocator &allocator() = 0;
 61  };
 62  
 63  
 64  //
 65  // Abstract Database Records.
 66  // Each database record type has a subclass of this.
 67  // These are RefCounted; you can hang on to them as long as you like,
 68  // stick (RefPointers to) them into maps, and so on. Just go for it.
 69  //
 70  class Record : public RefCount, public CssmAutoData {
 71  public:
 72  	Record() : CssmAutoData(Allocator::standard(Allocator::sensitive)) { }
 73  	Record(const char * const * attributeNames);	// sets mAttributes
 74  	virtual ~Record();
 75  	static const CSSM_DB_RECORDTYPE recordType = CSSM_DL_DB_RECORD_ANY;
 76  
 77  	void addAttributes(const char * const * attributeNames); // add more
 78  
 79  	// raw attribute access
 80  	CssmDbRecordAttributeData &attributes() { return mAttributes; }
 81  	const CssmDbRecordAttributeData &attributes() const { return mAttributes; }
 82  	CSSM_DB_RECORDTYPE actualRecordType() const { return mAttributes.recordType(); }
 83  	
 84  	CssmAutoData &recordData() { return *this; }	// my data nature
 85  
 86  protected:
 87  	CssmAutoDbRecordAttributeData mAttributes;
 88  };
 89  
 90  
 91  //
 92  // TableBase is an implementation class for template Table below.
 93  // Do not use it directly (you'll be sorry).
 94  // Continue reading at template Table below.
 95  //
 96  class TableBase {
 97  public:
 98  	DLAccess &database;
 99  
100  	CSSM_DB_RECORDTYPE recordType() const { return mRecordType; }
101  	void recordType(CSSM_DB_RECORDTYPE t) { mRecordType = t; }	// override
102  	
103  	// erase all elements matching a query
104  	uint32 erase(const CSSM_QUERY &query);
105  	uint32 erase(const Query &query);
106  
107  protected:
108  	TableBase(DLAccess &source, CSSM_DB_RECORDTYPE type, bool getData = true);
109  	
110  	class AccessRef : public RefCount {
111  	protected:
112  		AccessRef() : mAccess(NULL) { }
113  		AccessRef(DLAccess *ac) : mAccess(ac) { }
114  		DLAccess *mAccess;
115  	};
116  	
117  	struct Handle : public AccessRef {
118  		CSSM_HANDLE query;
119  		Handle(DLAccess *ac, CSSM_HANDLE q) : AccessRef(ac), query(q) { }
120  		~Handle();
121  	};
122  	
123  	struct Uid : public AccessRef {
124  		CSSM_DB_UNIQUE_RECORD *uid;
125  		Uid(DLAccess *ac, CSSM_DB_UNIQUE_RECORD *id) : AccessRef(ac), uid(id) { }
126  		~Uid();
127  	};
128  	
129  	class Iterator {
130  	public:
131  		const CSSM_DB_UNIQUE_RECORD *recordHandle() const
132  		{ assert(mUid); return mUid->uid; }
133  	
134  	protected:
135  		Iterator() { }
136  		Iterator(DLAccess *ac, CSSM_HANDLE query, CSSM_DB_UNIQUE_RECORD *id,
137  			Record *record, bool getData);
138  		void advance(Record *newRecord); // generic operator ++ helper
139  	
140  		DLAccess *mAccess;				// data source
141  		RefPointer<Handle> mQuery;		// DL/MDS query handle
142  		RefPointer<Uid> mUid;			// record unique identifier
143  		RefPointer<Record> mRecord;		// current record value
144  		bool mGetData;					// ask for data on iteration
145  	};
146  	
147  protected:
148  	CSSM_DB_RECORDTYPE mRecordType;		// CSSM/MDS record type
149  	bool mGetData;						// ask for record data on primary iteration
150  };
151  
152  
153  //
154  // A Table represents a single relation in a database (of some kind)
155  //
156  template <class RecordType>
157  class Table : private TableBase {
158  	typedef RefPointer<RecordType> RecPtr;
159  public:
160  	Table(DLAccess &source) : TableBase(source, RecordType::recordType) { }
161  	Table(DLAccess &source, CSSM_DB_RECORDTYPE type) : TableBase(source, type) { }
162  	Table(DLAccess &source, bool getData) : TableBase(source, RecordType::recordType, getData) { }
163  	
164  public:
165  	class iterator : public Iterator,
166  			public std::iterator<forward_iterator_tag, RefPointer<RecordType> > {
167  		friend class Table;
168  	public:
169  		iterator() { }
170  
171  		bool operator == (const iterator &other) const
172  		{ return mUid.get() == other.mUid.get(); }
173  		bool operator != (const iterator &other) const
174  		{ return mUid.get() != other.mUid.get(); }
175  
176  		RecPtr operator * () const { return static_cast<RecordType *>(mRecord.get()); }
177  		RecordType *operator -> () const { return static_cast<RecordType *>(mRecord.get()); }
178  		iterator operator ++ () { advance(new RecordType); return *this; }
179  		iterator operator ++ (int) { iterator old = *this; operator ++ (); return old; }
180  		
181  		void erase();
182  		
183  	private:
184  		iterator(DLAccess *ac, CSSM_HANDLE query, CSSM_DB_UNIQUE_RECORD *id,
185  			RecordType *record, bool getData)
186  			: Iterator(ac, query, id, record, getData) { }
187  	};
188  	
189  public:
190  	iterator begin();
191  	iterator find(const CSSM_QUERY &query);
192  	iterator find(const Query &query);
193  	iterator end()		{ return iterator(); }
194  	RecPtr fetch(const Query &query, CSSM_RETURN err = CSSM_OK) // one-stop shopping
195  	{ return fetchFirst(find(query), err); }
196  	RecPtr fetch(CSSM_RETURN err = CSSM_OK)	// fetch first of type
197  	{ return fetchFirst(begin(), err); }
198  
199  	// erase all records matching a query
200  	void erase(const CSSM_QUERY &query);
201  	void erase(const Query &query);
202  	
203  	void erase(iterator it) { it.erase(); }
204  	
205  private:
206  	iterator startQuery(const CssmQuery &query, bool getData);
207  	RecPtr fetchFirst(iterator it, CSSM_RETURN err);
208  };
209  
210  
211  //
212  // Template out-of-line functions
213  //
214  template <class RecordType>
215  typename Table<RecordType>::iterator Table<RecordType>::begin()
216  {
217  	return startQuery(CssmQuery(mRecordType), mGetData);
218  }
219  
220  template <class RecordType>
221  typename Table<RecordType>::iterator Table<RecordType>::find(const CSSM_QUERY &query)
222  {
223  	return startQuery(CssmQuery(CssmQuery::overlay(query), mRecordType), mGetData);
224  }
225  
226  template <class RecordType>
227  typename Table<RecordType>::iterator Table<RecordType>::find(const Query &query)
228  {
229  	return startQuery(CssmQuery(query.cssmQuery(), mRecordType), mGetData);
230  }
231  
232  template <class RecordType>
233  RefPointer<RecordType> Table<RecordType>::fetchFirst(iterator it, CSSM_RETURN err)
234  {
235  	if (it == end())
236  		if (err)
237  			CssmError::throwMe(err);
238  		else
239  			return NULL;
240  	else
241  		return *it;
242  }
243  
244  
245  template <class RecordType>
246  typename Table<RecordType>::iterator Table<RecordType>::startQuery(const CssmQuery &query, bool getData)
247  {
248  	RefPointer<RecordType> record = new RecordType;
249  	CSSM_DB_UNIQUE_RECORD *id;
250  	CssmAutoData data(database.allocator());
251  	CSSM_HANDLE queryHandle = database.dlGetFirst(query, record->attributes(),
252  		getData ? &data.get() : NULL, id);
253  	if (queryHandle == CSSM_INVALID_HANDLE)
254  		return end();  // not found
255  	if (getData)
256  		record->recordData() = data;
257  	return iterator(&database, queryHandle, id, record, getData);
258  }
259  
260  
261  template <class RecordType>
262  void Table<RecordType>::iterator::erase()
263  {
264  	mAccess->dlDeleteRecord(mUid->uid);
265  	mUid->uid = NULL;
266  }
267  
268  
269  } // end namespace CssmClient
270  } // end namespace Security
271  
272  #endif // _H_CDSA_CLIENT_DLITERATORS