]>
Commit | Line | Data |
---|---|---|
294beb6e A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2010 Apple Inc. All rights reserved. | |
4 | * | |
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | #include <CoreFoundation/CoreFoundation.h> | |
19 | #include <dns_sd.h> | |
20 | #include <UserEventAgentInterface.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | ||
24 | ||
25 | #pragma mark - | |
26 | #pragma mark Types | |
27 | #pragma mark - | |
28 | static const char* sPluginIdentifier = "com.apple.bonjour.events"; | |
29 | ||
30 | // PLIST Keys | |
31 | static const CFStringRef sServiceNameKey = CFSTR("ServiceName"); | |
32 | static const CFStringRef sServiceTypeKey = CFSTR("ServiceType"); | |
33 | static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain"); | |
34 | ||
35 | static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd"); | |
36 | static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove"); | |
37 | static const CFStringRef sWhileServiceExistsKey = CFSTR("WhileServiceExists"); | |
38 | ||
39 | static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken"); | |
40 | ||
41 | static const CFStringRef sPluginTimersKey = CFSTR("PluginTimers"); | |
42 | ||
43 | ||
44 | /************************************************ | |
45 | * Launch Event Dictionary (input from launchd) | |
46 | * Passed To: ManageEventsCallback | |
47 | *----------------------------------------------- | |
48 | * Typing in this dictionary is not enforced | |
49 | * above us. So this may not be true. Type check | |
50 | * all input before using it. | |
51 | *----------------------------------------------- | |
52 | * sServiceNameKey - CFString (Optional) | |
53 | * sServiceTypeKey - CFString | |
54 | * sServiceDomainKey - CFString | |
55 | * | |
56 | * One or more of the following. | |
57 | *----------------------------------- | |
58 | * sOnServiceAddKey - CFBoolean | |
59 | * sOnServiceRemoveKey - CFBoolean | |
60 | * sWhileServiceExistsKey - CFBoolean | |
61 | ************************************************/ | |
62 | ||
63 | /************************************************ | |
64 | * Browser Dictionary | |
65 | *----------------------------------------------- | |
66 | * sServiceDomainKey - CFString | |
67 | * sServiceTypeKey - CFString | |
68 | ************************************************/ | |
69 | ||
70 | /************************************************ | |
71 | * Event Dictionary | |
72 | *----------------------------------------------- | |
73 | * sServiceNameKey - CFString (Optional) | |
74 | * sLaunchdTokenKey - CFNumber | |
75 | ************************************************/ | |
76 | ||
77 | typedef struct { | |
78 | UserEventAgentInterfaceStruct* _UserEventAgentInterface; | |
79 | CFUUIDRef _factoryID; | |
80 | UInt32 _refCount; | |
81 | ||
82 | void* _pluginContext; | |
83 | ||
84 | CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries. | |
85 | CFMutableDictionaryRef _browsers; // A Dictionary of "Browser Dictionarys" where the resposible browser is the key. | |
86 | CFMutableDictionaryRef _onAddEvents; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service appearing. | |
87 | CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing. | |
88 | CFMutableDictionaryRef _whileServiceExist; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing. | |
89 | ||
90 | ||
91 | CFMutableArrayRef _timers; | |
92 | ||
93 | } BonjourUserEventsPlugin; | |
94 | ||
95 | ||
96 | typedef struct { | |
97 | ||
98 | CFIndex refCount; | |
99 | BonjourUserEventsPlugin* plugin; | |
100 | CFNumberRef token; | |
101 | ||
102 | } TimerContextInfo; | |
103 | ||
104 | typedef struct { | |
105 | CFIndex refCount; | |
106 | DNSServiceRef browserRef; | |
107 | } NetBrowserInfo; | |
108 | ||
109 | #pragma mark - | |
110 | #pragma mark Prototypes | |
111 | #pragma mark - | |
112 | // COM Stuff | |
113 | static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv); | |
114 | static ULONG AddRef(void* instance); | |
115 | static ULONG Release(void* instance); | |
116 | ||
117 | static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID); | |
118 | static void Dealloc(BonjourUserEventsPlugin* plugin); | |
119 | ||
120 | void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID); | |
121 | ||
122 | // Plugin Management | |
123 | static void Install(void* instance); | |
124 | static void ManageEventsCallback( | |
125 | UserEventAgentLaunchdAction action, | |
126 | CFNumberRef token, | |
127 | CFTypeRef eventMatchDict, | |
128 | void * vContext); | |
129 | ||
130 | ||
131 | // Plugin Guts | |
132 | void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters); | |
133 | void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken); | |
134 | ||
135 | NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain); | |
136 | NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef); | |
137 | void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key); | |
138 | void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken); | |
139 | ||
140 | // Net Service Browser Stuff | |
141 | void ServiceBrowserCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, const char* replyDomain, void* context); | |
142 | void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary); | |
143 | void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, Boolean didAppear); | |
144 | void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info ); | |
145 | ||
146 | // Convence Stuff | |
147 | const char* CStringFromCFString(CFStringRef string); | |
148 | ||
149 | ||
150 | // TimerContextInfo "Object" | |
151 | TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token); | |
152 | const void* TimerContextInfoRetain(const void* info); | |
153 | void TimerContextInfoRelease(const void* info); | |
154 | CFStringRef TimerContextInfoCopyDescription(const void* info); | |
155 | ||
156 | // NetBrowserInfo "Object" | |
157 | NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context); | |
158 | const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info); | |
159 | void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info); | |
160 | Boolean NetBrowserInfoEqual(const void *value1, const void *value2); | |
161 | CFHashCode NetBrowserInfoHash(const void *value); | |
162 | CFStringRef NetBrowserInfoCopyDescription(const void *value); | |
163 | ||
164 | ||
165 | static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = { | |
166 | 0, | |
167 | NetBrowserInfoRetain, | |
168 | NetBrowserInfoRelease, | |
169 | NetBrowserInfoCopyDescription, | |
170 | NetBrowserInfoEqual, | |
171 | NetBrowserInfoHash | |
172 | }; | |
173 | ||
174 | static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = { | |
175 | 0, | |
176 | NetBrowserInfoRetain, | |
177 | NetBrowserInfoRelease, | |
178 | NetBrowserInfoCopyDescription, | |
179 | NetBrowserInfoEqual | |
180 | }; | |
181 | ||
182 | // COM type definition goop. | |
183 | static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { | |
184 | NULL, // Required padding for COM | |
185 | QueryInterface, // Query Interface | |
186 | AddRef, // AddRef() | |
187 | Release, // Release() | |
188 | Install // Install | |
189 | }; | |
190 | ||
191 | #pragma mark - | |
192 | #pragma mark COM Management | |
193 | #pragma mark - | |
194 | ||
195 | /***************************************************************************** | |
196 | *****************************************************************************/ | |
197 | static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) | |
198 | { | |
199 | CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); | |
200 | ||
201 | // Test the requested ID against the valid interfaces. | |
202 | if(CFEqual(interfaceID, kUserEventAgentInterfaceID)) | |
203 | { | |
204 | ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
205 | *ppv = myInstance; | |
206 | CFRelease(interfaceID); | |
207 | return S_OK; | |
208 | } | |
209 | else if(CFEqual(interfaceID, IUnknownUUID)) | |
210 | { | |
211 | ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); | |
212 | *ppv = myInstance; | |
213 | CFRelease(interfaceID); | |
214 | return S_OK; | |
215 | } | |
216 | else // Requested interface unknown, bail with error. | |
217 | { | |
218 | *ppv = NULL; | |
219 | CFRelease(interfaceID); | |
220 | return E_NOINTERFACE; | |
221 | } | |
222 | } | |
223 | ||
224 | /***************************************************************************** | |
225 | *****************************************************************************/ | |
226 | static ULONG AddRef(void* instance) | |
227 | { | |
228 | BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; | |
229 | return ++plugin->_refCount; | |
230 | } | |
231 | ||
232 | /***************************************************************************** | |
233 | *****************************************************************************/ | |
234 | static ULONG Release(void* instance) | |
235 | { | |
236 | BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; | |
237 | ||
238 | if (plugin->_refCount != 0) | |
239 | --plugin->_refCount; | |
240 | ||
241 | if (plugin->_refCount == 0) | |
242 | { | |
243 | Dealloc(instance); | |
244 | return 0; | |
245 | } | |
246 | ||
247 | return plugin->_refCount; | |
248 | } | |
249 | ||
250 | /***************************************************************************** | |
251 | * Alloc | |
252 | * - | |
253 | * Functionas as both +[alloc] and -[init] for the plugin. Add any | |
254 | * initalization of member variables here. | |
255 | *****************************************************************************/ | |
256 | static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID) | |
257 | { | |
258 | BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin)); | |
259 | ||
260 | plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; | |
261 | plugin->_pluginContext = NULL; | |
262 | ||
263 | if (factoryID) | |
264 | { | |
265 | plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID); | |
266 | CFPlugInAddInstanceForFactory(factoryID); | |
267 | } | |
268 | ||
269 | plugin->_refCount = 1; | |
270 | plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks); | |
271 | plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); | |
272 | plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); | |
273 | plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); | |
274 | plugin->_whileServiceExist = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); | |
275 | ||
276 | plugin->_timers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
277 | ||
278 | return plugin; | |
279 | } | |
280 | ||
281 | /***************************************************************************** | |
282 | * Dealloc | |
283 | * - | |
284 | * Much like Obj-C dealloc this method is responsible for releasing any object | |
285 | * this plugin is holding. Unlike ObjC, you call directly free() instead of | |
286 | * [super dalloc]. | |
287 | *****************************************************************************/ | |
288 | static void Dealloc(BonjourUserEventsPlugin* plugin) | |
289 | { | |
290 | CFUUIDRef factoryID = plugin->_factoryID; | |
291 | ||
292 | if (factoryID) | |
293 | { | |
294 | CFPlugInRemoveInstanceForFactory(factoryID); | |
295 | CFRelease(factoryID); | |
296 | } | |
297 | ||
298 | if (plugin->_tokenToBrowserMap) | |
299 | CFRelease(plugin->_tokenToBrowserMap); | |
300 | ||
301 | if (plugin->_browsers) | |
302 | CFRelease(plugin->_browsers); | |
303 | ||
304 | if (plugin->_onAddEvents) | |
305 | CFRelease(plugin->_onAddEvents); | |
306 | ||
307 | if (plugin->_onRemoveEvents) | |
308 | CFRelease(plugin->_onRemoveEvents); | |
309 | ||
310 | if (plugin->_whileServiceExist) | |
311 | CFRelease(plugin->_whileServiceExist); | |
312 | ||
313 | if (plugin->_timers) | |
314 | { | |
315 | CFIndex i; | |
316 | CFIndex count = CFArrayGetCount(plugin->_timers); | |
317 | CFRunLoopRef crl = CFRunLoopGetCurrent(); | |
318 | ||
319 | for (i = 0; i < count; ++i) | |
320 | { | |
321 | CFRunLoopTimerRef timer = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(plugin->_timers, i); | |
322 | CFRunLoopRemoveTimer(crl, timer, kCFRunLoopCommonModes); | |
323 | } | |
324 | ||
325 | CFRelease(plugin->_timers); | |
326 | } | |
327 | ||
328 | free(plugin); | |
329 | } | |
330 | ||
331 | /******************************************************************************* | |
332 | *******************************************************************************/ | |
333 | void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) | |
334 | { | |
335 | (void)allocator; | |
336 | BonjourUserEventsPlugin * result = NULL; | |
337 | ||
338 | if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) { | |
339 | result = Alloc(kUserEventAgentFactoryID); | |
340 | } | |
341 | ||
342 | return (void *)result; | |
343 | } | |
344 | ||
345 | #pragma mark - | |
346 | #pragma mark Plugin Management | |
347 | #pragma mark - | |
348 | /***************************************************************************** | |
349 | * Install | |
350 | * - | |
351 | * This is invoked once when the plugin is loaded to do initial setup and | |
352 | * allow us to register with launchd. If UserEventAgent crashes, the plugin | |
353 | * will need to be reloaded, and hence this will get invoked again. | |
354 | *****************************************************************************/ | |
355 | static void Install(void *instance) | |
356 | { | |
357 | BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; | |
358 | ||
359 | plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin); | |
360 | ||
361 | if (!plugin->_pluginContext) | |
362 | { | |
363 | fprintf(stderr, "%s: failed to register for launch events.\n", sPluginIdentifier); | |
364 | return; | |
365 | } | |
366 | ||
367 | } | |
368 | ||
369 | /***************************************************************************** | |
370 | * ManageEventsCallback | |
371 | * - | |
372 | * This is invoked when launchd loads a event dictionary and needs to inform | |
373 | * us what a daemon / agent is looking for. | |
374 | *****************************************************************************/ | |
375 | static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext) | |
376 | { | |
377 | ||
378 | if (!eventMatchDict || CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID()) | |
379 | { | |
380 | fprintf(stderr, "%s given non-dictionary for event dictionary\n", sPluginIdentifier); | |
381 | return; | |
382 | } | |
383 | ||
384 | if (action == kUserEventAgentLaunchdAdd) | |
385 | { | |
386 | // Launchd wants us to add a launch event for this token and matching dictionary. | |
387 | AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict); | |
388 | } | |
389 | else if (action == kUserEventAgentLaunchdRemove) | |
390 | { | |
391 | // Launchd wants us to remove the event hook we setup for this token / matching dictionary. | |
392 | RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token); | |
393 | } | |
394 | else | |
395 | { | |
396 | fprintf(stderr, "%s got unknown UserEventAction: %d\n", sPluginIdentifier, action); | |
397 | } | |
398 | } | |
399 | ||
400 | ||
401 | #pragma mark - | |
402 | #pragma mark Plugin Guts | |
403 | #pragma mark - | |
404 | ||
405 | /***************************************************************************** | |
406 | * AddEventToPlugin | |
407 | * - | |
408 | * This method is invoked when launchd wishes the plugin to setup a launch | |
409 | * event matching the parameters in the dictionary. | |
410 | *****************************************************************************/ | |
411 | void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters) | |
412 | { | |
413 | CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey); | |
414 | CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey); | |
415 | CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey); | |
416 | CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey); | |
417 | CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey); | |
418 | CFBooleanRef cfWhileSericeExists = CFDictionaryGetValue(eventParameters, sWhileServiceExistsKey); | |
419 | ||
420 | Boolean onAdd = false; | |
421 | Boolean onRemove = false; | |
422 | Boolean whileExists = false; | |
423 | ||
424 | if (cfOnAdd && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd)) | |
425 | onAdd = true; | |
426 | ||
427 | if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove)) | |
428 | onRemove = true; | |
429 | ||
430 | if (cfWhileSericeExists && CFGetTypeID(cfWhileSericeExists) == CFBooleanGetTypeID() && CFBooleanGetValue(cfWhileSericeExists)) | |
431 | whileExists = true; | |
432 | ||
433 | // A type is required. If none is specified, BAIL | |
434 | if (!type || CFGetTypeID(type) != CFStringGetTypeID()) | |
435 | { | |
436 | fprintf(stderr, "%s, a LaunchEvent is missing a service type.\n", sPluginIdentifier); | |
437 | return; | |
438 | } | |
439 | ||
440 | // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore. | |
441 | if ((!onAdd && !onRemove && !whileExists) || (onAdd && onRemove && whileExists)) | |
442 | { | |
443 | fprintf(stderr, "%s, a LaunchEvent is missing both onAdd/onRemove/existance or has both.\n", sPluginIdentifier); | |
444 | return; | |
445 | } | |
446 | ||
447 | // If no domain is specified, assume local. | |
448 | if (!domain) | |
449 | { | |
450 | domain = CFSTR("local"); | |
451 | } | |
452 | else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fai; | |
453 | { | |
454 | fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier); | |
455 | return; | |
456 | } | |
457 | ||
458 | ||
459 | // If we have a name filter, but it's not a string. This event it broken, bail. | |
460 | if (name && CFGetTypeID(name) != CFStringGetTypeID()) | |
461 | { | |
462 | fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier); | |
463 | return; | |
464 | } | |
465 | ||
466 | // Get us a browser | |
467 | NetBrowserInfo* browser = CreateBrowserForTypeAndDomain(plugin, type, domain); | |
468 | ||
469 | if (!browser) | |
470 | { | |
471 | fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier); | |
472 | return; | |
473 | } | |
474 | ||
475 | // Create Event Dictionary | |
476 | CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
477 | ||
478 | ||
479 | CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken); | |
480 | ||
481 | if (name) | |
482 | CFDictionarySetValue(eventDictionary, sServiceNameKey, name); | |
483 | ||
484 | // Add to the correct dictionary. | |
485 | if (onAdd) | |
486 | AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser); | |
487 | ||
488 | if (onRemove) | |
489 | AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser); | |
490 | ||
491 | if (whileExists) | |
492 | AddEventDictionary(eventDictionary, plugin->_whileServiceExist, browser); | |
493 | ||
494 | // Add Token Mapping | |
495 | CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser); | |
496 | ||
497 | // Release Memory | |
498 | CFRelease(eventDictionary); | |
499 | NetBrowserInfoRelease(NULL, browser); | |
500 | ||
501 | } | |
502 | ||
503 | ||
504 | ||
505 | /***************************************************************************** | |
506 | * RemoveEventFromPlugin | |
507 | * - | |
508 | * This method is invoked when launchd wishes the plugin to setup a launch | |
509 | * event matching the parameters in the dictionary. | |
510 | *****************************************************************************/ | |
511 | void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken) | |
512 | { | |
513 | NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken); | |
514 | Boolean othersUsingBrowser = false; | |
515 | ||
516 | if (!browser) | |
517 | { | |
518 | long long value = 0; | |
519 | CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value); | |
520 | fprintf(stderr, "%s, Launchd asked us to remove a token we did not register!\nToken:%lld\n", sPluginIdentifier, value); | |
521 | return; | |
522 | } | |
523 | ||
524 | CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser); | |
525 | CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser); | |
526 | ||
527 | if (onAddEvents) | |
528 | { | |
529 | RemoveEventFromArray(onAddEvents, launchdToken); | |
530 | ||
531 | // Is the array now empty, clean up | |
532 | if (CFArrayGetCount(onAddEvents) == 0) | |
533 | CFDictionaryRemoveValue(plugin->_onAddEvents, browser); | |
534 | } | |
535 | ||
536 | if (onRemoveEvents) | |
537 | { | |
538 | RemoveEventFromArray(onRemoveEvents, launchdToken); | |
539 | ||
540 | // Is the array now empty, clean up | |
541 | if (CFArrayGetCount(onRemoveEvents) == 0) | |
542 | CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser); | |
543 | } | |
544 | ||
545 | // Remove ourselves from the token dictionary. | |
546 | CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); | |
547 | ||
548 | // Check to see if anyone else is using this browser. | |
549 | CFIndex i; | |
550 | CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap); | |
551 | NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); | |
552 | ||
553 | // Fetch the values of the token dictionary | |
554 | CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers); | |
555 | ||
556 | for (i = 0; i < count; ++i) | |
557 | { | |
558 | if (NetBrowserInfoEqual(browsers[i], browser)) | |
559 | { | |
560 | othersUsingBrowser = true; | |
561 | break; | |
562 | } | |
563 | } | |
564 | ||
565 | // If no one else is useing our browser, clean up! | |
566 | if (!othersUsingBrowser) | |
567 | { | |
568 | CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); // This triggers release and dealloc of the browser | |
569 | } | |
570 | ||
571 | free(browsers); | |
572 | } | |
573 | ||
574 | ||
575 | /***************************************************************************** | |
576 | * CreateBrowserForTypeAndDomain | |
577 | * - | |
578 | * This method returns a NetBrowserInfo that is looking for a type of | |
579 | * service in a domain. If no browser exists, it will create one and return it. | |
580 | *****************************************************************************/ | |
581 | NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain) | |
582 | { | |
583 | CFIndex i; | |
584 | CFIndex count = CFDictionaryGetCount(plugin->_browsers); | |
585 | NetBrowserInfo* browser = NULL; | |
586 | CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef)); | |
587 | NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); | |
588 | ||
589 | // Fetch the values of the browser dictionary | |
590 | CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts); | |
591 | ||
592 | // Loop thru the browsers list and see if we can find a matching one. | |
593 | for (i = 0; i < count; ++i) | |
594 | { | |
595 | CFDictionaryRef browserDict = dicts[i]; | |
596 | ||
597 | CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey); | |
598 | CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey); | |
599 | ||
600 | // If we have a matching browser, break | |
601 | if (CFStringCompare(browserType, type, kCFCompareCaseInsensitive) && | |
602 | CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive)) | |
603 | { | |
604 | browser = browsers[i]; | |
605 | NetBrowserInfoRetain(NULL, browser); | |
606 | break; | |
607 | } | |
608 | } | |
609 | ||
610 | // No match found, lets create one! | |
611 | if (!browser) | |
612 | { | |
613 | ||
614 | browser = NetBrowserInfoCreate(type, domain, plugin); | |
615 | ||
616 | if (!browser) | |
617 | { | |
618 | fprintf(stderr, "%s, failed to search for %s.%s", sPluginIdentifier, CStringFromCFString(type) , CStringFromCFString(domain)); | |
619 | free(dicts); | |
620 | free(browsers); | |
621 | return NULL; | |
622 | } | |
623 | ||
624 | // Service browser created, lets add this to ourselves to the dictionary. | |
625 | CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
626 | ||
627 | CFDictionarySetValue(browserDict, sServiceTypeKey, type); | |
628 | CFDictionarySetValue(browserDict, sServiceDomainKey, domain); | |
629 | ||
630 | // Add the dictionary to the browsers dictionary. | |
631 | CFDictionarySetValue(plugin->_browsers, browser, browserDict); | |
632 | ||
633 | // Release Memory | |
634 | CFRelease(browserDict); | |
635 | } | |
636 | ||
637 | free(dicts); | |
638 | free(browsers); | |
639 | ||
640 | return browser; | |
641 | } | |
642 | ||
643 | /***************************************************************************** | |
644 | * BrowserForSDRef | |
645 | * - | |
646 | * This method returns a NetBrowserInfo that matches the calling SDRef passed | |
647 | * in via the callback. | |
648 | *****************************************************************************/ | |
649 | NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef) | |
650 | { | |
651 | CFIndex i; | |
652 | CFIndex count = CFDictionaryGetCount(plugin->_browsers); | |
653 | NetBrowserInfo* browser = NULL; | |
654 | NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); | |
655 | ||
656 | // Fetch the values of the browser dictionary | |
657 | CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL); | |
658 | ||
659 | // Loop thru the browsers list and see if we can find a matching one. | |
660 | for (i = 0; i < count; ++i) | |
661 | { | |
662 | NetBrowserInfo* currentBrowser = browsers[i]; | |
663 | ||
664 | if (currentBrowser->browserRef == sdRef) | |
665 | { | |
666 | browser = currentBrowser; | |
667 | break; | |
668 | } | |
669 | } | |
670 | ||
671 | ||
672 | free(browsers); | |
673 | ||
674 | return browser; | |
675 | } | |
676 | ||
677 | /***************************************************************************** | |
678 | * AddEventDictionary | |
679 | * - | |
680 | * Adds a event to a browser's event dictionary | |
681 | *****************************************************************************/ | |
682 | ||
683 | void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key) | |
684 | { | |
685 | CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key); | |
686 | ||
687 | if (!eventsForBrowser) // We have no events for this browser yet, lets add him. | |
688 | { | |
689 | eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
690 | CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser); | |
691 | } | |
692 | else | |
693 | { | |
694 | CFRetain(eventsForBrowser); | |
695 | } | |
696 | ||
697 | CFArrayAppendValue(eventsForBrowser, eventDict); | |
698 | CFRelease(eventsForBrowser); | |
699 | } | |
700 | ||
701 | /***************************************************************************** | |
702 | * RemoveEventFromArray | |
703 | * - | |
704 | * Searches a Array of Event Dictionaries to find one with a matching launchd | |
705 | * token and remove it. | |
706 | *****************************************************************************/ | |
707 | ||
708 | void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken) | |
709 | { | |
710 | CFIndex i; | |
711 | CFIndex count = CFArrayGetCount(array); | |
712 | // Loop thru looking for us. | |
713 | for (i = 0; i < count; ) | |
714 | { | |
715 | CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i); | |
716 | CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey); | |
717 | ||
718 | if (CFEqual(token, launchdToken)) // This is the same event? | |
719 | { | |
720 | CFArrayRemoveValueAtIndex(array, i); // Remove the event, | |
721 | break; // The token should only exist once, so it make no sense to continue. | |
722 | } | |
723 | else | |
724 | { | |
725 | ++i; // If it's not us, advance. | |
726 | } | |
727 | } | |
728 | } | |
729 | ||
730 | #pragma mark - | |
731 | #pragma mark Net Service Browser Stuff | |
732 | #pragma mark - | |
733 | ||
734 | /***************************************************************************** | |
735 | * ServiceBrowserCallback | |
736 | * - | |
737 | * This method is the heart of the plugin. It's the runloop callback annoucing | |
738 | * the appearence and disappearance of network services. | |
739 | *****************************************************************************/ | |
740 | ||
741 | void ServiceBrowserCallback (DNSServiceRef sdRef, | |
742 | DNSServiceFlags flags, | |
743 | uint32_t interfaceIndex, | |
744 | DNSServiceErrorType errorCode, | |
745 | const char* serviceName, | |
746 | const char* regtype, | |
747 | const char* replyDomain, | |
748 | void* context ) | |
749 | { | |
750 | (void)interfaceIndex; | |
751 | (void)regtype; | |
752 | (void)replyDomain; | |
753 | BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context; | |
754 | NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef); | |
755 | ||
756 | if (!browser) // Missing browser? | |
757 | return; | |
758 | ||
759 | if (errorCode != kDNSServiceErr_NoError) | |
760 | return; | |
761 | ||
762 | CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8); | |
763 | ||
764 | if (flags & kDNSServiceFlagsAdd) | |
765 | { | |
766 | HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents); | |
767 | HandleStateEventsForService(plugin, browser, cfServiceName, true); | |
768 | } | |
769 | else | |
770 | { | |
771 | HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents); | |
772 | HandleStateEventsForService(plugin, browser, cfServiceName, false); | |
773 | } | |
774 | ||
775 | CFRelease(cfServiceName); | |
776 | } | |
777 | ||
778 | /***************************************************************************** | |
779 | * HandleTemporaryEventsForService | |
780 | * - | |
781 | * This method handles the firing of one shot events. Aka. Events that are | |
782 | * signaled when a service appears / disappears. They have a temporarly | |
783 | * signaled state. | |
784 | *****************************************************************************/ | |
785 | void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary) | |
786 | { | |
787 | CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in. | |
788 | CFIndex i; | |
789 | CFIndex count; | |
790 | ||
791 | if (!events) // Somehow we have a orphan browser... | |
792 | return; | |
793 | ||
794 | count = CFArrayGetCount(events); | |
795 | ||
796 | // Go thru the events and run filters, notifity if they pass. | |
797 | for (i = 0; i < count; ++i) | |
798 | { | |
799 | CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i); | |
800 | CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey); | |
801 | CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey); | |
802 | ||
803 | // Currently we only filter on service name, that makes this as simple as... | |
804 | if (!eventServiceName || CFEqual(serviceName, eventServiceName)) | |
805 | { | |
806 | // Create Context Info | |
807 | CFRunLoopTimerContext context; | |
808 | TimerContextInfo* info = TimerContextInfoCreate(plugin, token); | |
809 | ||
810 | context.version = 0; | |
811 | context.info = info; | |
812 | context.retain = TimerContextInfoRetain; | |
813 | context.release = TimerContextInfoRelease; | |
814 | context.copyDescription = TimerContextInfoCopyDescription; | |
815 | ||
816 | // Create and add one shot timer to flip the event off after a second | |
817 | CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 0, 0, 0, TemporaryEventTimerCallout, &context); | |
818 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); | |
819 | ||
820 | // Signal Event | |
821 | UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, true); | |
822 | ||
823 | // Clean Up | |
824 | TimerContextInfoRelease(info); | |
825 | CFRelease(timer); | |
826 | ||
827 | } | |
828 | } | |
829 | ||
830 | } | |
831 | ||
832 | /***************************************************************************** | |
833 | * HandleStateEventsForService | |
834 | * - | |
835 | * This method handles the toggling the state of a while exists event to | |
836 | * reflect the network. | |
837 | *****************************************************************************/ | |
838 | void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, Boolean didAppear) | |
839 | { | |
840 | CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(plugin->_whileServiceExist, browser); // Get the _whileServiceExist events that are interested in this browser. | |
841 | CFIndex i; | |
842 | CFIndex count; | |
843 | ||
844 | if (!events) // Somehow we have a orphan browser... | |
845 | return; | |
846 | ||
847 | count = CFArrayGetCount(events); | |
848 | ||
849 | // Go thru the events and run filters, notifity if they pass. | |
850 | for (i = 0; i < count; ++i) | |
851 | { | |
852 | CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i); | |
853 | CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey); | |
854 | CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey); | |
855 | ||
856 | // Currently we only filter on service name, that makes this as simple as... | |
857 | if (!eventServiceName || CFEqual(serviceName, eventServiceName)) | |
858 | UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, didAppear); | |
859 | } | |
860 | } | |
861 | ||
862 | /***************************************************************************** | |
863 | * TemporaryEventTimerCallout | |
864 | * - | |
865 | * This method is invoked a second after a watched service appears / disappears | |
866 | * to toggle the state of the launch event back to false. | |
867 | *****************************************************************************/ | |
868 | void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info ) | |
869 | { | |
870 | TimerContextInfo* contextInfo = (TimerContextInfo*)info; | |
871 | ||
872 | UserEventAgentSetLaunchEventState(contextInfo->plugin->_pluginContext, contextInfo->token, false); | |
873 | ||
874 | // Remove from pending timers array. | |
875 | CFIndex i; | |
876 | CFIndex count = CFArrayGetCount(contextInfo->plugin->_timers); | |
877 | ||
878 | for (i = 0; i < count; ++i) | |
879 | { | |
880 | CFRunLoopTimerRef item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(contextInfo->plugin->_timers, i); | |
881 | ||
882 | if (item == timer) | |
883 | break; | |
884 | } | |
885 | ||
886 | if (i != count) | |
887 | CFArrayRemoveValueAtIndex(contextInfo->plugin->_timers, i); | |
888 | } | |
889 | ||
890 | #pragma mark - | |
891 | #pragma mark Convenence | |
892 | #pragma mark - | |
893 | ||
894 | /***************************************************************************** | |
895 | * CStringFromCFString | |
896 | * - | |
897 | * Silly convenence function for dealing with non-critical CFSTR -> cStr | |
898 | * conversions. | |
899 | *****************************************************************************/ | |
900 | ||
901 | const char* CStringFromCFString(CFStringRef string) | |
902 | { | |
903 | const char* defaultString = "??????"; | |
904 | const char* cstring; | |
905 | ||
906 | if (!string) | |
907 | return defaultString; | |
908 | ||
909 | cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); | |
910 | ||
911 | return (cstring) ? cstring : defaultString; | |
912 | ||
913 | } | |
914 | ||
915 | #pragma mark - | |
916 | #pragma mark TimerContextInfo "Object" | |
917 | #pragma mark - | |
918 | ||
919 | /***************************************************************************** | |
920 | * TimerContextInfoCreate | |
921 | * - | |
922 | * Convenence for creating TimerContextInfo pseudo-objects | |
923 | *****************************************************************************/ | |
924 | TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token) | |
925 | { | |
926 | TimerContextInfo* info = malloc(sizeof(TimerContextInfo)); | |
927 | ||
928 | info->refCount = 1; | |
929 | info->plugin = plugin; | |
930 | info->token = (CFNumberRef)CFRetain(token); | |
931 | ||
932 | return info; | |
933 | } | |
934 | ||
935 | /***************************************************************************** | |
936 | * TimerContextInfoRetain | |
937 | * - | |
938 | * Convenence for retaining TimerContextInfo pseudo-objects | |
939 | *****************************************************************************/ | |
940 | const void* TimerContextInfoRetain(const void* info) | |
941 | { | |
942 | TimerContextInfo* context = (TimerContextInfo*)info; | |
943 | ||
944 | if (!context) | |
945 | return NULL; | |
946 | ||
947 | ++context->refCount; | |
948 | ||
949 | return context; | |
950 | } | |
951 | ||
952 | /***************************************************************************** | |
953 | * TimerContextInfoRelease | |
954 | * - | |
955 | * Convenence for releasing TimerContextInfo pseudo-objects | |
956 | *****************************************************************************/ | |
957 | void TimerContextInfoRelease(const void* info) | |
958 | { | |
959 | TimerContextInfo* context = (TimerContextInfo*)info; | |
960 | ||
961 | if (!context) | |
962 | return; | |
963 | ||
964 | if (context->refCount == 1) | |
965 | { | |
966 | CFRelease(context->token); | |
967 | free(context); | |
968 | return; | |
969 | } | |
970 | else | |
971 | { | |
972 | --context->refCount; | |
973 | } | |
974 | } | |
975 | ||
976 | /***************************************************************************** | |
977 | * TimerContextInfoCopyDescription | |
978 | * - | |
979 | * This method actually does nothing, but is just a stub so CF is happy. | |
980 | *****************************************************************************/ | |
981 | CFStringRef TimerContextInfoCopyDescription(const void* info) | |
982 | { | |
983 | (void)info; | |
984 | return CFStringCreateWithCString(NULL, "TimerContextInfo: No useful description", kCFStringEncodingUTF8); | |
985 | } | |
986 | ||
987 | ||
988 | #pragma mark - | |
989 | #pragma mark NetBrowserInfo "Object" | |
990 | #pragma mark - | |
991 | /***************************************************************************** | |
992 | * NetBrowserInfoCreate | |
993 | * - | |
994 | * The method creates a NetBrowserInfo Object and initalizes it. | |
995 | *****************************************************************************/ | |
996 | NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context) | |
997 | { | |
998 | NetBrowserInfo* outObj = NULL; | |
999 | DNSServiceRef browserRef = NULL; | |
1000 | char* cServiceType = NULL; | |
1001 | char* cDomain = NULL; | |
1002 | Boolean success = true; | |
1003 | ||
1004 | CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8); | |
1005 | cServiceType = calloc(serviceSize, 1); | |
1006 | success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8); | |
1007 | ||
1008 | if (domain) | |
1009 | { | |
1010 | CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8); | |
1011 | cDomain = calloc(serviceSize, 1); | |
1012 | success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); | |
1013 | } | |
1014 | ||
1015 | if (!success) | |
1016 | { | |
1017 | fprintf(stderr, "LaunchEvent has badly encoded service type or domain.\n"); | |
1018 | free(cServiceType); | |
1019 | ||
1020 | if (cDomain) | |
1021 | free(cDomain); | |
1022 | ||
1023 | return NULL; | |
1024 | } | |
1025 | ||
1026 | DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context); | |
1027 | ||
1028 | if (err != kDNSServiceErr_NoError) | |
1029 | { | |
1030 | fprintf(stderr, "Failed to create browser for %s, %s\n", cServiceType, cDomain); | |
1031 | free(cServiceType); | |
1032 | ||
1033 | if (cDomain) | |
1034 | free(cDomain); | |
1035 | ||
1036 | return NULL; | |
1037 | } | |
1038 | ||
1039 | DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue()); | |
1040 | ||
1041 | ||
1042 | outObj = malloc(sizeof(NetBrowserInfo)); | |
1043 | ||
1044 | outObj->refCount = 1; | |
1045 | outObj->browserRef = browserRef; | |
1046 | ||
1047 | free(cServiceType); | |
1048 | ||
1049 | if (cDomain) | |
1050 | free(cDomain); | |
1051 | ||
1052 | return outObj; | |
1053 | } | |
1054 | ||
1055 | /***************************************************************************** | |
1056 | * NetBrowserInfoRetain | |
1057 | * - | |
1058 | * The method retains a NetBrowserInfo object. | |
1059 | *****************************************************************************/ | |
1060 | const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info) | |
1061 | { | |
1062 | (void)allocator; | |
1063 | NetBrowserInfo* obj = (NetBrowserInfo*)info; | |
1064 | ||
1065 | if (!obj) | |
1066 | return NULL; | |
1067 | ||
1068 | ++obj->refCount; | |
1069 | ||
1070 | return obj; | |
1071 | } | |
1072 | ||
1073 | /***************************************************************************** | |
1074 | * NetBrowserInfoRelease | |
1075 | * - | |
1076 | * The method releases a NetBrowserInfo object. | |
1077 | *****************************************************************************/ | |
1078 | void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info) | |
1079 | { | |
1080 | (void)allocator; | |
1081 | NetBrowserInfo* obj = (NetBrowserInfo*)info; | |
1082 | ||
1083 | if (!obj) | |
1084 | return; | |
1085 | ||
1086 | if (obj->refCount == 1) | |
1087 | { | |
1088 | DNSServiceRefDeallocate(obj->browserRef); | |
1089 | free(obj); | |
1090 | } | |
1091 | else | |
1092 | { | |
1093 | --obj->refCount; | |
1094 | } | |
1095 | ||
1096 | } | |
1097 | ||
1098 | /***************************************************************************** | |
1099 | * NetBrowserInfoEqual | |
1100 | * - | |
1101 | * The method is used to compare two NetBrowserInfo objects for equality. | |
1102 | *****************************************************************************/ | |
1103 | Boolean NetBrowserInfoEqual(const void *value1, const void *value2) | |
1104 | { | |
1105 | NetBrowserInfo* obj1 = (NetBrowserInfo*)value1; | |
1106 | NetBrowserInfo* obj2 = (NetBrowserInfo*)value2; | |
1107 | ||
1108 | if (obj1->browserRef == obj2->browserRef) | |
1109 | return true; | |
1110 | ||
1111 | return false; | |
1112 | } | |
1113 | ||
1114 | /***************************************************************************** | |
1115 | * NetBrowserInfoHash | |
1116 | * - | |
1117 | * The method is used to make a hash for the object. We can cheat and use the | |
1118 | * browser pointer. | |
1119 | *****************************************************************************/ | |
1120 | CFHashCode NetBrowserInfoHash(const void *value) | |
1121 | { | |
1122 | return (CFHashCode)((NetBrowserInfo*)value)->browserRef; | |
1123 | } | |
1124 | ||
1125 | ||
1126 | /***************************************************************************** | |
1127 | * NetBrowserInfoCopyDescription | |
1128 | * - | |
1129 | * Make CF happy. | |
1130 | *****************************************************************************/ | |
1131 | CFStringRef NetBrowserInfoCopyDescription(const void *value) | |
1132 | { | |
1133 | (void)value; | |
1134 | return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8); | |
1135 | } |