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