1/*
2 * Copyright (c) 2000-2004 Apple Computer, 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
38namespace Security {
39namespace 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//
47class DLAccess {
48public:
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//
70class Record : public RefCount, public CssmAutoData {
71public:
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
86protected:
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//
96class TableBase {
97public:
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
107protected:
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
147protected:
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//
156template <class RecordType>
157class Table : private TableBase {
158	typedef RefPointer<RecordType> RecPtr;
159public:
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
164public:
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
189public:
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
205private:
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//
214template <class RecordType>
215typename Table<RecordType>::iterator Table<RecordType>::begin()
216{
217	return startQuery(CssmQuery(mRecordType), mGetData);
218}
219
220template <class RecordType>
221typename Table<RecordType>::iterator Table<RecordType>::find(const CSSM_QUERY &query)
222{
223	return startQuery(CssmQuery(CssmQuery::overlay(query), mRecordType), mGetData);
224}
225
226template <class RecordType>
227typename Table<RecordType>::iterator Table<RecordType>::find(const Query &query)
228{
229	return startQuery(CssmQuery(query.cssmQuery(), mRecordType), mGetData);
230}
231
232template <class RecordType>
233RefPointer<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
245template <class RecordType>
246typename 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
261template <class RecordType>
262void 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
273