#include "ODBridge.h"
static const char* kEmailName = "Alias";
static const char* kPrintName = "PrintName";
static const char *dbname = "Open Directory Data Library";
static long long OD_query_time_out = 5LL * NSEC_PER_SEC;
CSSM_RETURN cssme_for_OD(CFErrorRef ODReturn)
{
switch (CFErrorGetCode(ODReturn))
{
case 0:
return CSSM_OK;
default:
return CSSMERR_DL_INTERNAL_ERROR;
}
}
DirectoryService::DirectoryService()
{
this->db_name = (char *)dbname;
CFErrorRef ODReturn;
this->all_open_queries = CFArrayCreateMutable(kCFAllocatorDefault, 5, NULL);
this->query_dispatch_queue = dispatch_queue_create("com.apple.ldapdl", NULL);
dispatch_retain(this->query_dispatch_queue);
if((this->node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeContacts, &ODReturn)) == NULL)
throw DirectoryServiceException (ODReturn);
}
DirectoryService::~DirectoryService()
{
CFIndex i, count;
for (i = 0, count = CFArrayGetCount(this->all_open_queries); i < count; i++) {
ODdl_results_handle result;
result = (ODdl_results_handle) CFArrayGetValueAtIndex(this->all_open_queries, i);
CFRelease(result->query);
CFRelease(result->certificates);
CFRelease(result->searchString);
free(result);
}
CFRelease(this->node);
}
#define LINEWIDTH 80
#define BYTESPERBLOB 4
#define BUFFERSIZE (LINEWIDTH+1)
#define BLOBSPERLINE (LINEWIDTH / ((BYTESPERBLOB * 2) +1))
#define BYTESPERLINE (BLOBSPERLINE * BYTESPERBLOB)
static void
getCertsFromArray(const void *value, void *context)
{
ODdl_results_handle results = (ODdl_results_handle) context;
CFRetain((CFDataRef) value);
CFArrayAppendValue(results->certificates, value);
}
void
cert_query_callback(ODQueryRef query, CFArrayRef qresults, CFErrorRef error, void *context)
{
ODdl_results_handle results = (ODdl_results_handle) context;
__block CFErrorRef retError;
dispatch_sync(results->result_modifier_queue, ^{
if (qresults == NULL) {
if(error == NULL) {
if(results->results_done) dispatch_semaphore_signal(results->results_done);
}
return;
}
CFIndex i, count;
for (i = 0, count = CFArrayGetCount(qresults); i < count; i++) {
ODRecordRef rec = (ODRecordRef)CFArrayGetValueAtIndex(qresults, i);
CFArrayRef certs = ODRecordCopyValues(rec, kODAttributeTypeUserCertificate, &retError);
if(certs && CFArrayGetCount(certs)) {
CFArrayApplyFunction(certs, CFRangeMake( 0, CFArrayGetCount(certs)) , getCertsFromArray, context);
CFRelease(certs);
}
}
});
error = retError;
}
static bool isValidEmailString(CFStringRef s)
{
char buf[256];
if(CFStringGetCString(s, buf, 256, kCFStringEncodingASCII) == 0) return false;
if(CFStringGetLength(s) < 10) return false; if(index(buf, '@') == NULL) return false;
if(index(buf, '.') == NULL) return false;
return true;
}
ODdl_results_handle
DirectoryService::translate_cssm_query_to_OD_query(const CSSM_QUERY *Query, CSSM_RETURN *error)
{
ODMatchType matchType;
CFIndex ODmaxResults = 0;
ODdl_results_handle results;
int i, searchPred;
CFErrorRef ODerror;
*error = 0;
CSSM_SELECTION_PREDICATE_PTR pred;
for (i=0, searchPred = -1; searchPred == -1 && i < (int) Query->NumSelectionPredicates; i++) {
pred = (CSSM_SELECTION_PREDICATE_PTR) &(Query->SelectionPredicate[i]);
if(pred->Attribute.Info.AttributeNameFormat == CSSM_DB_ATTRIBUTE_NAME_AS_STRING) {
if(strncmp(pred->Attribute.Info.Label.AttributeName, kEmailName, strlen(kEmailName)) == 0 ||
strncmp(pred->Attribute.Info.Label.AttributeName, kEmailName, strlen(kPrintName)) == 0) {
searchPred = i;
}
}
}
if (searchPred == -1) {
*error = CSSMERR_DL_INVALID_QUERY;
return NULL;
}
pred = (CSSM_SELECTION_PREDICATE_PTR) &(Query->SelectionPredicate[searchPred]);
if (pred->Attribute.NumberOfValues != 1) {
*error = CSSMERR_DL_INVALID_QUERY;
return NULL;
}
switch(pred->DbOperator) {
case CSSM_DB_EQUAL:
case CSSM_DB_LESS_THAN:
case CSSM_DB_GREATER_THAN:
case CSSM_DB_CONTAINS:
case CSSM_DB_CONTAINS_INITIAL_SUBSTRING:
case CSSM_DB_CONTAINS_FINAL_SUBSTRING:
matchType = kODMatchEqualTo;
break;
default:
*error = CSSMERR_DL_INVALID_QUERY;
return NULL;
}
if((results = (ODdl_results_handle) malloc(sizeof(struct ODdl_results))) == NULL) {
*error = CSSMERR_DL_MEMORY_ERROR;
return NULL;
}
CFArrayAppendValue(this->all_open_queries, (const void *)results);
results->recordid = CSSM_DL_DB_RECORD_X509_CERTIFICATE;
results->certificates = CFArrayCreateMutable(NULL, 20, &kCFTypeArrayCallBacks);
results->currentRecord = 0;
results->results_done = dispatch_semaphore_create(0);
results->result_modifier_queue = dispatch_queue_create("com.apple.ldapdl.results", NULL);
if (Query->QueryLimits.SizeLimit != CSSM_QUERY_SIZELIMIT_NONE) ODmaxResults = Query->QueryLimits.SizeLimit;
results->searchString = CFStringCreateWithBytes(NULL, pred->Attribute.Value->Data, pred->Attribute.Value->Length, kCFStringEncodingUTF8, false);
if(!isValidEmailString(results->searchString)) {
*error = CSSMERR_DL_ENDOFDATA;
return NULL;
}
if((results->query = ODQueryCreateWithNode(NULL, this->node, kODRecordTypeUsers, kODAttributeTypeEMailAddress, matchType, results->searchString, kODAttributeTypeUserCertificate, ODmaxResults, &ODerror)) == NULL) {
*error = cssme_for_OD(ODerror);
return NULL;
}
ODQuerySetCallback(results->query, cert_query_callback, (void *)results);
ODQuerySetDispatchQueue( results->query, query_dispatch_queue ); dispatch_semaphore_wait(results->results_done, dispatch_time(DISPATCH_TIME_NOW, OD_query_time_out));
return results;
}
CFDataRef
DirectoryService::getNextCertFromResults(ODdl_results_handle results)
{
__block CFDataRef retval;
if(results->result_modifier_queue == NULL) {
return NULL;
}
dispatch_sync(results->result_modifier_queue, ^{
if(!results || !results->certificates) {
retval = NULL;
} else if(results->currentRecord < CFArrayGetCount(results->certificates)) {
retval = (CFDataRef) CFArrayGetValueAtIndex(results->certificates, results->currentRecord);
results->currentRecord++;
} else {
retval = NULL;
}
});
return retval;
}