Commit | Line | Data |
---|---|---|
009ee3c6 | 1 | /* |
a40a14f8 | 2 | * Copyright (c) 2003, 2004, 2006-2008 Apple Inc. All rights reserved. |
009ee3c6 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
009ee3c6 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * Modification History | |
26 | * | |
27 | * April 23, 2003 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | ||
32 | #include "configd.h" | |
33 | #include "pattern.h" | |
34 | ||
35 | ||
36 | /* | |
37 | * Notes: | |
38 | * | |
39 | * - pattern matching information is maintained in a dictionary (patternData). | |
40 | * - the dictionary "key" is the regular expression being matched | |
41 | * - the dictionary "value" is a CFArray with the following contents | |
42 | * [0] = CFData consisting of the pre-compiled regular expression | |
43 | * [1] = CFArray[CFNumber] consisting of the sessions watching this pattern | |
44 | * [2-n] = dynamic store keys which match this pattern | |
45 | */ | |
46 | ||
47 | ||
48 | typedef struct { | |
49 | CFMutableArrayRef pInfo; | |
50 | regex_t *preg; | |
51 | } addContext, *addContextRef; | |
52 | ||
53 | ||
54 | static __inline__ void | |
55 | my_CFDictionaryApplyFunction(CFDictionaryRef theDict, | |
56 | CFDictionaryApplierFunction applier, | |
57 | void *context) | |
58 | { | |
59 | CFAllocatorRef myAllocator; | |
60 | CFDictionaryRef myDict; | |
61 | ||
62 | myAllocator = CFGetAllocator(theDict); | |
63 | myDict = CFDictionaryCreateCopy(myAllocator, theDict); | |
64 | CFDictionaryApplyFunction(myDict, applier, context); | |
65 | CFRelease(myDict); | |
66 | return; | |
67 | } | |
68 | ||
69 | ||
70 | static void | |
71 | identifyKeyForPattern(const void *key, void *val, void *context) | |
72 | { | |
73 | CFStringRef storeKey = (CFStringRef)key; | |
74 | CFDictionaryRef storeValue = (CFDictionaryRef)val; | |
75 | CFMutableArrayRef pInfo = ((addContextRef)context)->pInfo; | |
76 | regex_t * preg = ((addContextRef)context)->preg; | |
77 | ||
78 | CFIndex len; | |
79 | int reError; | |
80 | char str_q[256]; | |
81 | char * str = str_q; | |
82 | ||
83 | if (!CFDictionaryContainsKey(storeValue, kSCDData)) { | |
84 | /* if no data (yet) */ | |
85 | return; | |
86 | } | |
87 | ||
88 | /* convert store key to C string */ | |
edebe297 | 89 | len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1; |
009ee3c6 A |
90 | if (len > (CFIndex)sizeof(str_q)) |
91 | str = CFAllocatorAllocate(NULL, len, 0); | |
92 | if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) { | |
dbf6a266 | 93 | SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern(): could not convert store key to C string")); |
009ee3c6 A |
94 | goto done; |
95 | } | |
96 | ||
97 | /* compare store key to new notification keys regular expression pattern */ | |
98 | reError = regexec(preg, str, 0, NULL, 0); | |
99 | switch (reError) { | |
100 | case 0 : | |
101 | /* we've got a match */ | |
102 | CFArrayAppendValue(pInfo, storeKey); | |
103 | break; | |
104 | case REG_NOMATCH : | |
105 | /* no match */ | |
106 | break; | |
107 | default : { | |
108 | char reErrBuf[256]; | |
109 | ||
110 | (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
dbf6a266 | 111 | SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern regexec(): %s"), reErrBuf); |
009ee3c6 A |
112 | break; |
113 | } | |
114 | } | |
115 | ||
116 | done : | |
117 | ||
118 | if (str != str_q) CFAllocatorDeallocate(NULL, str); | |
119 | return; | |
120 | } | |
121 | ||
122 | ||
edebe297 | 123 | static Boolean |
009ee3c6 A |
124 | patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error) |
125 | { | |
126 | Boolean append = FALSE; | |
127 | Boolean insert = FALSE; | |
128 | CFIndex len = 0; | |
129 | Boolean ok; | |
130 | char str_q[256]; | |
131 | char * str = str_q; | |
132 | ||
133 | if (!CFStringHasPrefix(pattern, CFSTR("^"))) { | |
134 | insert = TRUE; | |
135 | } | |
136 | ||
137 | if (!CFStringHasSuffix(pattern, CFSTR("$")) || | |
138 | CFStringHasSuffix(pattern, CFSTR("\\$"))) { | |
139 | append = TRUE; | |
140 | } | |
141 | ||
142 | /* if regex pattern is not bounded at both ends */ | |
143 | if (insert || append) { | |
144 | pattern = CFStringCreateWithFormat(NULL, | |
145 | NULL, | |
146 | CFSTR("%s%@%s"), | |
147 | insert ? "^" : "", | |
148 | pattern, | |
149 | append ? "$" : ""); | |
150 | } | |
151 | ||
152 | (void)CFStringGetBytes(pattern, | |
153 | CFRangeMake(0, CFStringGetLength(pattern)), | |
154 | kCFStringEncodingASCII, | |
155 | 0, | |
156 | FALSE, | |
157 | NULL, | |
158 | 0, | |
159 | &len); | |
160 | if (++len > (CFIndex)sizeof(str_q)) { | |
161 | str = CFAllocatorAllocate(NULL, len, 0); | |
162 | } | |
163 | ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL); | |
164 | if (insert || append) { | |
165 | CFRelease(pattern); | |
166 | } | |
167 | if (ok) { | |
168 | int reError; | |
169 | ||
170 | reError = regcomp(preg, str, REG_EXTENDED); | |
171 | if (reError != 0) { | |
172 | char reErrBuf[256]; | |
173 | ||
174 | (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
175 | *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII); | |
dbf6a266 A |
176 | #ifdef DEBUG |
177 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("patternCompile regcomp(%s) failed: %s"), str, reErrBuf); | |
178 | #endif /* DEBUG */ | |
009ee3c6 A |
179 | ok = FALSE; |
180 | } | |
181 | } else { | |
182 | *error = CFRetain(CFSTR("could not convert pattern to regex string")); | |
dbf6a266 | 183 | #ifdef DEBUG |
009ee3c6 | 184 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error); |
dbf6a266 | 185 | #endif /* DEBUG */ |
009ee3c6 A |
186 | } |
187 | ||
188 | if (str != str_q) CFAllocatorDeallocate(NULL, str); | |
189 | return ok; | |
190 | } | |
191 | ||
192 | ||
edebe297 | 193 | static CFMutableArrayRef |
009ee3c6 A |
194 | patternCopy(CFStringRef pattern) |
195 | { | |
196 | CFArrayRef pInfo; | |
197 | ||
198 | pInfo = CFDictionaryGetValue(patternData, pattern); | |
199 | return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL; | |
200 | } | |
201 | ||
202 | ||
edebe297 | 203 | static CFMutableArrayRef |
009ee3c6 A |
204 | patternNew(CFStringRef pattern) |
205 | { | |
206 | addContext context; | |
edebe297 | 207 | CFStringRef err = NULL; |
009ee3c6 A |
208 | CFMutableArrayRef pInfo; |
209 | CFMutableDataRef pRegex; | |
210 | CFArrayRef pSessions; | |
211 | ||
212 | /* create the pattern info */ | |
213 | pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
214 | ||
215 | /* compile the regular expression from the pattern string. */ | |
216 | pRegex = CFDataCreateMutable(NULL, sizeof(regex_t)); | |
217 | CFDataSetLength(pRegex, sizeof(regex_t)); | |
218 | if (!patternCompile(pattern, (regex_t *)CFDataGetBytePtr(pRegex), &err)) { | |
219 | CFRelease(err); | |
220 | CFRelease(pRegex); | |
221 | CFRelease(pInfo); | |
222 | return NULL; | |
223 | } | |
224 | ||
225 | /* add the compiled regular expression to the pattern info */ | |
226 | CFArrayAppendValue(pInfo, pRegex); | |
227 | ||
228 | /* add the initial (empty) list of sessions watching this pattern */ | |
229 | pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); | |
230 | CFArrayAppendValue(pInfo, pSessions); | |
231 | CFRelease(pSessions); | |
232 | ||
233 | /* identify/add all existing keys that match the specified pattern */ | |
234 | context.pInfo = pInfo; | |
235 | context.preg = (regex_t *)CFDataGetBytePtr(pRegex); | |
236 | my_CFDictionaryApplyFunction(storeData, | |
237 | (CFDictionaryApplierFunction)identifyKeyForPattern, | |
238 | &context); | |
239 | ||
240 | CFRelease(pRegex); | |
241 | return pInfo; | |
242 | } | |
243 | ||
244 | ||
edebe297 A |
245 | __private_extern__ |
246 | CFArrayRef | |
247 | patternCopyMatches(CFStringRef pattern) | |
248 | { | |
249 | Boolean isNew = FALSE; | |
250 | CFArrayRef keys; | |
251 | CFMutableArrayRef pInfo; | |
252 | ||
253 | /* find (or create new instance of) this pattern */ | |
254 | pInfo = patternCopy(pattern); | |
255 | if (pInfo == NULL) { | |
256 | /* if new pattern */ | |
257 | pInfo = patternNew(pattern); | |
258 | if (pInfo == NULL) { | |
259 | return NULL; | |
260 | } | |
261 | ||
262 | isNew = TRUE; | |
263 | } | |
264 | ||
265 | if (isNew) { | |
266 | CFDataRef pRegex; | |
267 | ||
268 | pRegex = CFArrayGetValueAtIndex(pInfo, 0); | |
269 | regfree((regex_t *)CFDataGetBytePtr(pRegex)); | |
270 | } | |
271 | ||
272 | CFArrayReplaceValues(pInfo, CFRangeMake(0, 2), NULL, 0); | |
273 | keys = CFArrayCreateCopy(NULL, pInfo); | |
274 | CFRelease(pInfo); | |
275 | ||
276 | return keys; | |
277 | } | |
278 | ||
279 | ||
009ee3c6 A |
280 | __private_extern__ |
281 | Boolean | |
282 | patternAddSession(CFStringRef pattern, CFNumberRef sessionNum) | |
283 | { | |
284 | CFIndex i; | |
285 | CFIndex n; | |
286 | CFMutableArrayRef pInfo; | |
287 | CFMutableArrayRef pSessions; | |
288 | ||
289 | /* find (or create new instance of) this pattern */ | |
290 | pInfo = patternCopy(pattern); | |
edebe297 | 291 | if (pInfo == NULL) { |
009ee3c6 A |
292 | /* if new pattern */ |
293 | pInfo = patternNew(pattern); | |
edebe297 | 294 | if (pInfo == NULL) { |
009ee3c6 A |
295 | return FALSE; |
296 | } | |
297 | } | |
298 | ||
299 | /* add this session as a pattern watcher */ | |
300 | pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1); | |
301 | pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions); | |
302 | CFArrayAppendValue(pSessions, sessionNum); | |
303 | CFArraySetValueAtIndex(pInfo, 1, pSessions); | |
304 | CFRelease(pSessions); | |
305 | ||
306 | /* update pattern watcher info */ | |
307 | CFDictionarySetValue(patternData, pattern, pInfo); | |
308 | ||
309 | /* add this session as a watcher of any existing keys */ | |
310 | n = CFArrayGetCount(pInfo); | |
311 | for (i = 2; i < n; i++) { | |
312 | CFStringRef matchingKey; | |
313 | ||
314 | matchingKey = CFArrayGetValueAtIndex(pInfo, i); | |
315 | _addWatcher(sessionNum, matchingKey); | |
316 | } | |
317 | ||
318 | CFRelease(pInfo); | |
319 | return TRUE; | |
320 | } | |
321 | ||
322 | ||
323 | __private_extern__ | |
324 | void | |
325 | patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum) | |
326 | { | |
327 | CFIndex i; | |
328 | CFIndex n; | |
329 | CFMutableArrayRef pInfo; | |
330 | CFDataRef pRegex; | |
331 | CFMutableArrayRef pSessions; | |
332 | ||
333 | /* find instance of this pattern */ | |
334 | pInfo = patternCopy(pattern); | |
335 | ||
336 | /* remove this session as a watcher from all matching keys */ | |
337 | n = CFArrayGetCount(pInfo); | |
338 | for (i = 2; i < n; i++) { | |
339 | CFStringRef matchingKey; | |
340 | ||
341 | matchingKey = CFArrayGetValueAtIndex(pInfo, i); | |
342 | _removeWatcher(sessionNum, matchingKey); | |
343 | } | |
344 | ||
345 | /* remove session from watchers */ | |
346 | pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1); | |
347 | n = CFArrayGetCount(pSessions); | |
348 | if (n > 1) { | |
349 | /* if other sessions are watching this pattern */ | |
350 | ||
351 | pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions); | |
352 | i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum); | |
353 | CFArrayRemoveValueAtIndex(pSessions, i); | |
354 | CFArraySetValueAtIndex(pInfo, 1, pSessions); | |
355 | CFRelease(pSessions); | |
356 | ||
357 | CFDictionarySetValue(patternData, pattern, pInfo); | |
358 | } else { | |
359 | /* if no other sessions are watching this pattern */ | |
360 | ||
361 | pRegex = CFArrayGetValueAtIndex(pInfo, 0); | |
362 | regfree((regex_t *)CFDataGetBytePtr(pRegex)); | |
363 | CFDictionaryRemoveValue(patternData, pattern); | |
364 | } | |
365 | ||
366 | CFRelease(pInfo); | |
367 | return; | |
368 | } | |
369 | ||
370 | ||
371 | static void | |
372 | addKeyForPattern(const void *key, void *val, void *context) | |
373 | { | |
374 | CFStringRef pattern = (CFStringRef)key; | |
375 | CFArrayRef pInfo = (CFArrayRef)val; | |
376 | CFStringRef storeKey = (CFStringRef)context; | |
377 | ||
378 | CFIndex len; | |
379 | regex_t *preg; | |
380 | int reError; | |
381 | char str_q[256]; | |
382 | char * str = str_q; | |
383 | ||
384 | /* convert store key to C string */ | |
edebe297 | 385 | len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1; |
009ee3c6 A |
386 | if (len > (CFIndex)sizeof(str_q)) |
387 | str = CFAllocatorAllocate(NULL, len, 0); | |
388 | if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) { | |
dbf6a266 | 389 | SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern(): could not convert store key to C string")); |
009ee3c6 A |
390 | goto done; |
391 | } | |
392 | ||
393 | /* compare new store key to regular expression pattern */ | |
394 | preg = (regex_t *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0)); | |
395 | reError = regexec(preg, str, 0, NULL, 0); | |
396 | switch (reError) { | |
397 | case 0 : { | |
398 | /* | |
399 | * we've got a match | |
400 | */ | |
401 | CFIndex i; | |
402 | CFIndex n; | |
403 | CFMutableArrayRef pInfo_new; | |
404 | CFArrayRef pSessions; | |
405 | ||
406 | /* add watchers */ | |
407 | pSessions = CFArrayGetValueAtIndex(pInfo, 1); | |
408 | n = CFArrayGetCount(pSessions); | |
409 | for (i = 0; i < n; i++) { | |
410 | CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i); | |
411 | ||
412 | _addWatcher(sessionNum, storeKey); | |
413 | } | |
414 | ||
415 | /* add key, update pattern watcher info */ | |
416 | pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo); | |
417 | CFArrayAppendValue(pInfo_new, storeKey); | |
418 | CFDictionarySetValue(patternData, pattern, pInfo_new); | |
419 | CFRelease(pInfo_new); | |
420 | break; | |
421 | } | |
422 | case REG_NOMATCH : | |
423 | /* no match */ | |
424 | break; | |
425 | default : { | |
426 | char reErrBuf[256]; | |
427 | ||
428 | (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
dbf6a266 | 429 | SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern regexec(): %s"), reErrBuf); |
009ee3c6 A |
430 | break; |
431 | } | |
432 | } | |
433 | ||
434 | done : | |
435 | ||
436 | if (str != str_q) CFAllocatorDeallocate(NULL, str); | |
437 | return; | |
438 | } | |
439 | ||
440 | ||
441 | __private_extern__ | |
442 | void | |
443 | patternAddKey(CFStringRef key) | |
444 | { | |
a40a14f8 A |
445 | void *context = (void *)key; |
446 | ||
009ee3c6 A |
447 | my_CFDictionaryApplyFunction(patternData, |
448 | (CFDictionaryApplierFunction)addKeyForPattern, | |
a40a14f8 | 449 | context); |
009ee3c6 A |
450 | |
451 | return; | |
452 | } | |
453 | ||
454 | ||
455 | static void | |
456 | removeKeyFromPattern(const void *key, void *val, void *context) | |
457 | { | |
458 | CFStringRef pattern = (CFStringRef)key; | |
459 | CFArrayRef pInfo = (CFArrayRef)val; | |
460 | CFStringRef storeKey = (CFStringRef)context; | |
461 | ||
462 | CFIndex i; | |
463 | CFIndex n; | |
464 | CFMutableArrayRef pInfo_new; | |
465 | CFArrayRef pSessions; | |
466 | ||
467 | n = CFArrayGetCount(pInfo); | |
468 | if (n <= 2) { | |
469 | /* if no keys match this pattern */ | |
470 | return; | |
471 | } | |
472 | ||
473 | i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey); | |
dbf6a266 | 474 | if (i == kCFNotFound) { |
009ee3c6 A |
475 | /* if this key wasn't matched by this pattern */ |
476 | return; | |
477 | } | |
478 | ||
479 | /* remove key from pattern info */ | |
480 | pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo); | |
481 | CFArrayRemoveValueAtIndex(pInfo_new, i); | |
482 | ||
483 | /* remove watchers */ | |
484 | pSessions = CFArrayGetValueAtIndex(pInfo_new, 1); | |
485 | n = CFArrayGetCount(pSessions); | |
486 | for (i = 0; i < n; i++) { | |
487 | CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i); | |
488 | ||
489 | _removeWatcher(sessionNum, storeKey); | |
490 | } | |
491 | ||
492 | CFDictionarySetValue(patternData, pattern, pInfo_new); | |
493 | CFRelease(pInfo_new); | |
494 | return; | |
495 | } | |
496 | ||
497 | ||
498 | __private_extern__ | |
499 | void | |
500 | patternRemoveKey(CFStringRef key) | |
501 | { | |
a40a14f8 A |
502 | void *context = (void *)key; |
503 | ||
009ee3c6 A |
504 | my_CFDictionaryApplyFunction(patternData, |
505 | (CFDictionaryApplierFunction)removeKeyFromPattern, | |
a40a14f8 | 506 | context); |
009ee3c6 A |
507 | |
508 | return; | |
509 | } |