--- /dev/null
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Modification History
+ *
+ * April 23, 2003 Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+
+#include "configd.h"
+#include "pattern.h"
+
+
+/*
+ * Notes:
+ *
+ * - pattern matching information is maintained in a dictionary (patternData).
+ * - the dictionary "key" is the regular expression being matched
+ * - the dictionary "value" is a CFArray with the following contents
+ * [0] = CFData consisting of the pre-compiled regular expression
+ * [1] = CFArray[CFNumber] consisting of the sessions watching this pattern
+ * [2-n] = dynamic store keys which match this pattern
+ */
+
+
+typedef struct {
+ CFMutableArrayRef pInfo;
+ regex_t *preg;
+} addContext, *addContextRef;
+
+
+static __inline__ void
+my_CFDictionaryApplyFunction(CFDictionaryRef theDict,
+ CFDictionaryApplierFunction applier,
+ void *context)
+{
+ CFAllocatorRef myAllocator;
+ CFDictionaryRef myDict;
+
+ myAllocator = CFGetAllocator(theDict);
+ myDict = CFDictionaryCreateCopy(myAllocator, theDict);
+ CFDictionaryApplyFunction(myDict, applier, context);
+ CFRelease(myDict);
+ return;
+}
+
+
+static void
+identifyKeyForPattern(const void *key, void *val, void *context)
+{
+ CFStringRef storeKey = (CFStringRef)key;
+ CFDictionaryRef storeValue = (CFDictionaryRef)val;
+ CFMutableArrayRef pInfo = ((addContextRef)context)->pInfo;
+ regex_t * preg = ((addContextRef)context)->preg;
+
+ CFIndex len;
+ int reError;
+ char str_q[256];
+ char * str = str_q;
+
+ if (!CFDictionaryContainsKey(storeValue, kSCDData)) {
+ /* if no data (yet) */
+ return;
+ }
+
+ /* convert store key to C string */
+ len = CFStringGetLength(storeKey) + 1;
+ if (len > (CFIndex)sizeof(str_q))
+ str = CFAllocatorAllocate(NULL, len, 0);
+ if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("could not convert store key to C string"));
+ goto done;
+ }
+
+ /* compare store key to new notification keys regular expression pattern */
+ reError = regexec(preg, str, 0, NULL, 0);
+ switch (reError) {
+ case 0 :
+ /* we've got a match */
+ CFArrayAppendValue(pInfo, storeKey);
+ break;
+ case REG_NOMATCH :
+ /* no match */
+ break;
+ default : {
+ char reErrBuf[256];
+
+ (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+ break;
+ }
+ }
+
+ done :
+
+ if (str != str_q) CFAllocatorDeallocate(NULL, str);
+ return;
+}
+
+
+__private_extern__ Boolean
+patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error)
+{
+ Boolean append = FALSE;
+ Boolean insert = FALSE;
+ CFIndex len = 0;
+ Boolean ok;
+ char str_q[256];
+ char * str = str_q;
+
+ if (!CFStringHasPrefix(pattern, CFSTR("^"))) {
+ insert = TRUE;
+ }
+
+ if (!CFStringHasSuffix(pattern, CFSTR("$")) ||
+ CFStringHasSuffix(pattern, CFSTR("\\$"))) {
+ append = TRUE;
+ }
+
+ /* if regex pattern is not bounded at both ends */
+ if (insert || append) {
+ pattern = CFStringCreateWithFormat(NULL,
+ NULL,
+ CFSTR("%s%@%s"),
+ insert ? "^" : "",
+ pattern,
+ append ? "$" : "");
+ }
+
+ (void)CFStringGetBytes(pattern,
+ CFRangeMake(0, CFStringGetLength(pattern)),
+ kCFStringEncodingASCII,
+ 0,
+ FALSE,
+ NULL,
+ 0,
+ &len);
+ if (++len > (CFIndex)sizeof(str_q)) {
+ str = CFAllocatorAllocate(NULL, len, 0);
+ }
+ ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL);
+ if (insert || append) {
+ CFRelease(pattern);
+ }
+ if (ok) {
+ int reError;
+
+ reError = regcomp(preg, str, REG_EXTENDED);
+ if (reError != 0) {
+ char reErrBuf[256];
+
+ (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+ *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII);
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regcomp(%s) failed: %s"), str, reErrBuf);
+ ok = FALSE;
+ }
+ } else {
+ *error = CFRetain(CFSTR("could not convert pattern to regex string"));
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error);
+ }
+
+ if (str != str_q) CFAllocatorDeallocate(NULL, str);
+ return ok;
+}
+
+
+__private_extern__
+CFMutableArrayRef
+patternCopy(CFStringRef pattern)
+{
+ CFArrayRef pInfo;
+
+ pInfo = CFDictionaryGetValue(patternData, pattern);
+ return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL;
+}
+
+
+__private_extern__
+CFMutableArrayRef
+patternNew(CFStringRef pattern)
+{
+ addContext context;
+ CFStringRef err;
+ CFMutableArrayRef pInfo;
+ CFMutableDataRef pRegex;
+ CFArrayRef pSessions;
+
+ /* create the pattern info */
+ pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ /* compile the regular expression from the pattern string. */
+ pRegex = CFDataCreateMutable(NULL, sizeof(regex_t));
+ CFDataSetLength(pRegex, sizeof(regex_t));
+ if (!patternCompile(pattern, (regex_t *)CFDataGetBytePtr(pRegex), &err)) {
+ CFRelease(err);
+ CFRelease(pRegex);
+ CFRelease(pInfo);
+ return NULL;
+ }
+
+ /* add the compiled regular expression to the pattern info */
+ CFArrayAppendValue(pInfo, pRegex);
+
+ /* add the initial (empty) list of sessions watching this pattern */
+ pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(pInfo, pSessions);
+ CFRelease(pSessions);
+
+ /* identify/add all existing keys that match the specified pattern */
+ context.pInfo = pInfo;
+ context.preg = (regex_t *)CFDataGetBytePtr(pRegex);
+ my_CFDictionaryApplyFunction(storeData,
+ (CFDictionaryApplierFunction)identifyKeyForPattern,
+ &context);
+
+ CFRelease(pRegex);
+ return pInfo;
+}
+
+
+__private_extern__
+Boolean
+patternAddSession(CFStringRef pattern, CFNumberRef sessionNum)
+{
+ CFIndex i;
+ CFIndex n;
+ CFMutableArrayRef pInfo;
+ CFMutableArrayRef pSessions;
+
+ /* find (or create new instance of) this pattern */
+ pInfo = patternCopy(pattern);
+ if (!pInfo) {
+ /* if new pattern */
+ pInfo = patternNew(pattern);
+ if (!pInfo) {
+ return FALSE;
+ }
+ }
+
+ /* add this session as a pattern watcher */
+ pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
+ pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
+ CFArrayAppendValue(pSessions, sessionNum);
+ CFArraySetValueAtIndex(pInfo, 1, pSessions);
+ CFRelease(pSessions);
+
+ /* update pattern watcher info */
+ CFDictionarySetValue(patternData, pattern, pInfo);
+
+ /* add this session as a watcher of any existing keys */
+ n = CFArrayGetCount(pInfo);
+ for (i = 2; i < n; i++) {
+ CFStringRef matchingKey;
+
+ matchingKey = CFArrayGetValueAtIndex(pInfo, i);
+ _addWatcher(sessionNum, matchingKey);
+ }
+
+ CFRelease(pInfo);
+ return TRUE;
+}
+
+
+__private_extern__
+void
+patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum)
+{
+ CFIndex i;
+ CFIndex n;
+ CFMutableArrayRef pInfo;
+ CFDataRef pRegex;
+ CFMutableArrayRef pSessions;
+
+ /* find instance of this pattern */
+ pInfo = patternCopy(pattern);
+
+ /* remove this session as a watcher from all matching keys */
+ n = CFArrayGetCount(pInfo);
+ for (i = 2; i < n; i++) {
+ CFStringRef matchingKey;
+
+ matchingKey = CFArrayGetValueAtIndex(pInfo, i);
+ _removeWatcher(sessionNum, matchingKey);
+ }
+
+ /* remove session from watchers */
+ pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
+ n = CFArrayGetCount(pSessions);
+ if (n > 1) {
+ /* if other sessions are watching this pattern */
+
+ pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
+ i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum);
+ CFArrayRemoveValueAtIndex(pSessions, i);
+ CFArraySetValueAtIndex(pInfo, 1, pSessions);
+ CFRelease(pSessions);
+
+ CFDictionarySetValue(patternData, pattern, pInfo);
+ } else {
+ /* if no other sessions are watching this pattern */
+
+ pRegex = CFArrayGetValueAtIndex(pInfo, 0);
+ regfree((regex_t *)CFDataGetBytePtr(pRegex));
+ CFDictionaryRemoveValue(patternData, pattern);
+ }
+
+ CFRelease(pInfo);
+ return;
+}
+
+
+static void
+addKeyForPattern(const void *key, void *val, void *context)
+{
+ CFStringRef pattern = (CFStringRef)key;
+ CFArrayRef pInfo = (CFArrayRef)val;
+ CFStringRef storeKey = (CFStringRef)context;
+
+ CFIndex len;
+ regex_t *preg;
+ int reError;
+ char str_q[256];
+ char * str = str_q;
+
+ /* convert store key to C string */
+ len = CFStringGetLength(storeKey) + 1;
+ if (len > (CFIndex)sizeof(str_q))
+ str = CFAllocatorAllocate(NULL, len, 0);
+ if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("could not convert store key to C string"));
+ goto done;
+ }
+
+ /* compare new store key to regular expression pattern */
+ preg = (regex_t *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0));
+ reError = regexec(preg, str, 0, NULL, 0);
+ switch (reError) {
+ case 0 : {
+ /*
+ * we've got a match
+ */
+ CFIndex i;
+ CFIndex n;
+ CFMutableArrayRef pInfo_new;
+ CFArrayRef pSessions;
+
+ /* add watchers */
+ pSessions = CFArrayGetValueAtIndex(pInfo, 1);
+ n = CFArrayGetCount(pSessions);
+ for (i = 0; i < n; i++) {
+ CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
+
+ _addWatcher(sessionNum, storeKey);
+ }
+
+ /* add key, update pattern watcher info */
+ pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
+ CFArrayAppendValue(pInfo_new, storeKey);
+ CFDictionarySetValue(patternData, pattern, pInfo_new);
+ CFRelease(pInfo_new);
+ break;
+ }
+ case REG_NOMATCH :
+ /* no match */
+ break;
+ default : {
+ char reErrBuf[256];
+
+ (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
+ SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
+ break;
+ }
+ }
+
+ done :
+
+ if (str != str_q) CFAllocatorDeallocate(NULL, str);
+ return;
+}
+
+
+__private_extern__
+void
+patternAddKey(CFStringRef key)
+{
+ my_CFDictionaryApplyFunction(patternData,
+ (CFDictionaryApplierFunction)addKeyForPattern,
+ (void *)key);
+
+ return;
+}
+
+
+static void
+removeKeyFromPattern(const void *key, void *val, void *context)
+{
+ CFStringRef pattern = (CFStringRef)key;
+ CFArrayRef pInfo = (CFArrayRef)val;
+ CFStringRef storeKey = (CFStringRef)context;
+
+ CFIndex i;
+ CFIndex n;
+ CFMutableArrayRef pInfo_new;
+ CFArrayRef pSessions;
+
+ n = CFArrayGetCount(pInfo);
+ if (n <= 2) {
+ /* if no keys match this pattern */
+ return;
+ }
+
+ i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey);
+ if (i == -1) {
+ /* if this key wasn't matched by this pattern */
+ return;
+ }
+
+ /* remove key from pattern info */
+ pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
+ CFArrayRemoveValueAtIndex(pInfo_new, i);
+
+ /* remove watchers */
+ pSessions = CFArrayGetValueAtIndex(pInfo_new, 1);
+ n = CFArrayGetCount(pSessions);
+ for (i = 0; i < n; i++) {
+ CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
+
+ _removeWatcher(sessionNum, storeKey);
+ }
+
+ CFDictionarySetValue(patternData, pattern, pInfo_new);
+ CFRelease(pInfo_new);
+ return;
+}
+
+
+__private_extern__
+void
+patternRemoveKey(CFStringRef key)
+{
+ my_CFDictionaryApplyFunction(patternData,
+ (CFDictionaryApplierFunction)removeKeyFromPattern,
+ (void *)key);
+
+ return;
+}