]> git.saurik.com Git - apple/cf.git/blob - CFUserNotification.c
CF-550.tar.gz
[apple/cf.git] / CFUserNotification.c
1 /*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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 /* CFUserNotification.c
24 Copyright (c) 2000-2009, Apple Inc. All rights reserved.
25 Responsibility: Doug Davidson
26 */
27
28 #include <CoreFoundation/CFUserNotification.h>
29 #include <CoreFoundation/CFPropertyList.h>
30 #include <CoreFoundation/CFNumber.h>
31 #include <CoreFoundation/CFRunLoop.h>
32 #include "CFInternal.h"
33 #include <CoreFoundation/CFMachPort.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <mach/mach.h>
38 #include <mach/error.h>
39 #include <bootstrap_priv.h>
40 #include <limits.h>
41 #include <errno.h>
42 #include <pthread.h>
43
44
45 #define CFUserNotificationLog(alertHeader, alertMessage) CFLog(3, CFSTR("%@: %@"), alertHeader, alertMessage);
46
47 enum {
48 kCFUserNotificationCancelFlag = (1 << 3),
49 kCFUserNotificationUpdateFlag = (1 << 4)
50 };
51
52 CONST_STRING_DECL(kCFUserNotificationTokenKey, "Token")
53 CONST_STRING_DECL(kCFUserNotificationTimeoutKey, "Timeout")
54 CONST_STRING_DECL(kCFUserNotificationFlagsKey, "Flags")
55 CONST_STRING_DECL(kCFUserNotificationIconPathKey, "IconPath")
56 CONST_STRING_DECL(kCFUserNotificationSoundPathKey, "SoundPath")
57 CONST_STRING_DECL(kCFUserNotificationLocalizationPathKey, "LocalizationPath")
58 CONST_STRING_DECL(kCFUserNotificationAlertSourceKey, "AlertSource")
59 CONST_STRING_DECL(kCFUserNotificationTextFieldLabelsKey, "TextFieldTitles")
60 CONST_STRING_DECL(kCFUserNotificationCheckBoxLabelsKey, "CheckBoxTitles")
61 CONST_STRING_DECL(kCFUserNotificationIconURLKey, "IconURL")
62 CONST_STRING_DECL(kCFUserNotificationSoundURLKey, "SoundURL")
63 CONST_STRING_DECL(kCFUserNotificationLocalizationURLKey, "LocalizationURL")
64 CONST_STRING_DECL(kCFUserNotificationAlertHeaderKey, "AlertHeader")
65 CONST_STRING_DECL(kCFUserNotificationAlertMessageKey, "AlertMessage")
66 CONST_STRING_DECL(kCFUserNotificationDefaultButtonTitleKey, "DefaultButtonTitle")
67 CONST_STRING_DECL(kCFUserNotificationAlternateButtonTitleKey, "AlternateButtonTitle")
68 CONST_STRING_DECL(kCFUserNotificationOtherButtonTitleKey, "OtherButtonTitle")
69 CONST_STRING_DECL(kCFUserNotificationProgressIndicatorValueKey, "ProgressIndicatorValue")
70 CONST_STRING_DECL(kCFUserNotificationSessionIDKey, "SessionID")
71 CONST_STRING_DECL(kCFUserNotificationPopUpTitlesKey, "PopUpTitles")
72 CONST_STRING_DECL(kCFUserNotificationTextFieldTitlesKey, "TextFieldTitles")
73 CONST_STRING_DECL(kCFUserNotificationCheckBoxTitlesKey, "CheckBoxTitles")
74 CONST_STRING_DECL(kCFUserNotificationTextFieldValuesKey, "TextFieldValues")
75 CONST_STRING_DECL(kCFUserNotificationPopUpSelectionKey, "PopUpSelection")
76 CONST_STRING_DECL(kCFUserNotificationKeyboardTypesKey, "KeyboardTypes")
77 CONST_STRING_DECL(kCFUserNotificationAlertTopMostKey, "AlertTopMost") // boolean value
78
79
80 static CFTypeID __kCFUserNotificationTypeID = _kCFRuntimeNotATypeID;
81
82 struct __CFUserNotification {
83 CFRuntimeBase _base;
84 SInt32 _replyPort;
85 SInt32 _token;
86 CFTimeInterval _timeout;
87 CFOptionFlags _requestFlags;
88 CFOptionFlags _responseFlags;
89 CFStringRef _sessionID;
90 CFDictionaryRef _responseDictionary;
91 CFMachPortRef _machPort;
92 CFUserNotificationCallBack _callout;
93 };
94
95 static CFStringRef __CFUserNotificationCopyDescription(CFTypeRef cf) {
96 CFMutableStringRef result;
97 result = CFStringCreateMutable(CFGetAllocator(cf), 0);
98 CFStringAppendFormat(result, NULL, CFSTR("<CFUserNotification %p>"), cf);
99 return result;
100 }
101
102 #define MAX_STRING_LENGTH PATH_MAX
103 #define MAX_STRING_COUNT 16
104 #define MAX_PORT_NAME_LENGTH 63
105 #define NOTIFICATION_PORT_NAME_SUFFIX ".session."
106 #define MESSAGE_TIMEOUT 100
107 #if DEPLOYMENT_TARGET_MACOSX
108 #define NOTIFICATION_PORT_NAME "com.apple.UNCUserNotification"
109 #elif DEPLOYMENT_TARGET_EMBEDDED
110 #define NOTIFICATION_PORT_NAME "com.apple.SBUserNotification"
111 #else
112 #error Unknown or unspecified DEPLOYMENT_TARGET
113 #endif
114
115
116 static void __CFUserNotificationDeallocate(CFTypeRef cf);
117
118 static const CFRuntimeClass __CFUserNotificationClass = {
119 0,
120 "CFUserNotification",
121 NULL, // init
122 NULL, // copy
123 __CFUserNotificationDeallocate,
124 NULL, // equal
125 NULL, // hash
126 NULL, //
127 __CFUserNotificationCopyDescription
128 };
129
130 __private_extern__ void __CFUserNotificationInitialize(void) {
131 __kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass);
132 }
133
134 CFTypeID CFUserNotificationGetTypeID(void) {
135 if (_kCFRuntimeNotATypeID == __kCFUserNotificationTypeID) __CFUserNotificationInitialize();
136 return __kCFUserNotificationTypeID;
137 }
138
139 static void __CFUserNotificationDeallocate(CFTypeRef cf) {
140 CFUserNotificationRef userNotification = (CFUserNotificationRef)cf;
141 if (userNotification->_machPort) {
142 CFMachPortInvalidate(userNotification->_machPort);
143 CFRelease(userNotification->_machPort);
144 } else if (MACH_PORT_NULL != userNotification->_replyPort) {
145 mach_port_destroy(mach_task_self(), userNotification->_replyPort);
146 }
147 if (userNotification->_sessionID) CFRelease(userNotification->_sessionID);
148 if (userNotification->_responseDictionary) CFRelease(userNotification->_responseDictionary);
149 }
150
151 static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) {
152 if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
153 }
154
155 static CFDictionaryRef _CFUserNotificationModifiedDictionary(CFAllocatorRef allocator, CFDictionaryRef dictionary, SInt32 token, SInt32 timeout, CFStringRef source) {
156 CFMutableDictionaryRef md = CFDictionaryCreateMutable(allocator, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
157 CFNumberRef tokenNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &token);
158 CFNumberRef timeoutNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &timeout);
159 CFURLRef url = NULL;
160 CFStringRef path = NULL;
161
162 if (dictionary) CFDictionaryApplyFunction(dictionary, _CFUserNotificationAddToDictionary, md);
163 if (source) CFDictionaryAddValue(md, kCFUserNotificationAlertSourceKey, source);
164 if (tokenNumber) {
165 CFDictionaryAddValue(md, kCFUserNotificationTokenKey, tokenNumber);
166 CFRelease(tokenNumber);
167 }
168 if (timeoutNumber) {
169 CFDictionaryAddValue(md, kCFUserNotificationTimeoutKey, timeoutNumber);
170 CFRelease(timeoutNumber);
171 }
172
173 url = CFDictionaryGetValue(md, kCFUserNotificationIconURLKey);
174 if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
175 url = CFURLCopyAbsoluteURL(url);
176 CFDictionaryRemoveValue(md, kCFUserNotificationIconURLKey);
177 path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
178 CFDictionaryAddValue(md, kCFUserNotificationIconPathKey, path);
179 CFRelease(url);
180 CFRelease(path);
181 }
182 url = CFDictionaryGetValue(md, kCFUserNotificationSoundURLKey);
183 if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
184 url = CFURLCopyAbsoluteURL(url);
185 CFDictionaryRemoveValue(md, kCFUserNotificationSoundURLKey);
186 path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
187 CFDictionaryAddValue(md, kCFUserNotificationSoundPathKey, path);
188 CFRelease(url);
189 CFRelease(path);
190 }
191 url = CFDictionaryGetValue(md, kCFUserNotificationLocalizationURLKey);
192 if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
193 url = CFURLCopyAbsoluteURL(url);
194 CFDictionaryRemoveValue(md, kCFUserNotificationLocalizationURLKey);
195 path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
196 CFDictionaryAddValue(md, kCFUserNotificationLocalizationPathKey, path);
197 CFRelease(url);
198 CFRelease(path);
199 }
200 return md;
201 }
202
203 static SInt32 _CFUserNotificationSendRequest(CFAllocatorRef allocator, CFStringRef sessionID, mach_port_t replyPort, SInt32 token, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) {
204 CFDictionaryRef modifiedDictionary = NULL;
205 SInt32 retval = ERR_SUCCESS, itimeout = (timeout > 0.0 && timeout < INT_MAX) ? (SInt32)timeout : 0;
206 CFDataRef data;
207 mach_msg_base_t *msg = NULL;
208 mach_port_t bootstrapPort = MACH_PORT_NULL, serverPort = MACH_PORT_NULL;
209 CFIndex size;
210 char namebuffer[MAX_PORT_NAME_LENGTH + 1];
211
212 strlcpy(namebuffer, NOTIFICATION_PORT_NAME, sizeof(namebuffer));
213 if (sessionID) {
214 char sessionid[MAX_PORT_NAME_LENGTH + 1];
215 CFIndex len = MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX);
216 CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, (uint8_t *)sessionid, len, &size);
217 sessionid[len - 1] = '\0';
218 strlcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX, sizeof(namebuffer));
219 strlcat(namebuffer, sessionid, sizeof(namebuffer));
220 }
221
222 retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort);
223 if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up2(bootstrapPort, namebuffer, &serverPort, 0, 0);
224 if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) {
225 modifiedDictionary = _CFUserNotificationModifiedDictionary(allocator, dictionary, token, itimeout, _CFProcessNameString());
226 if (modifiedDictionary) {
227 data = CFPropertyListCreateXMLData(allocator, modifiedDictionary);
228 if (data) {
229 size = sizeof(mach_msg_base_t) + ((CFDataGetLength(data) + 3) & (~0x3));
230 msg = (mach_msg_base_t *)CFAllocatorAllocate(allocator, size, 0);
231 if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)");
232 if (msg) {
233 memset(msg, 0, size);
234 msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
235 msg->header.msgh_size = size;
236 msg->header.msgh_remote_port = serverPort;
237 msg->header.msgh_local_port = replyPort;
238 msg->header.msgh_id = flags;
239 msg->body.msgh_descriptor_count = 0;
240 CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (uint8_t *)msg + sizeof(mach_msg_base_t));
241 //CFShow(CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false));
242 retval = mach_msg((mach_msg_header_t *)msg, MACH_SEND_MSG|MACH_SEND_TIMEOUT, size, 0, MACH_PORT_NULL, MESSAGE_TIMEOUT, MACH_PORT_NULL);
243 CFAllocatorDeallocate(allocator, msg);
244 } else {
245 retval = unix_err(ENOMEM);
246 }
247 CFRelease(data);
248 } else {
249 retval = unix_err(ENOMEM);
250 }
251 CFRelease(modifiedDictionary);
252 } else {
253 retval = unix_err(ENOMEM);
254 }
255 }
256 return retval;
257 }
258
259 CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) {
260 CHECK_FOR_FORK();
261 CFUserNotificationRef userNotification = NULL;
262 SInt32 retval = ERR_SUCCESS;
263 static uint16_t tokenCounter = 0;
264 SInt32 token = ((getpid() << 16) | (tokenCounter++));
265 CFStringRef sessionID = (dictionary ? CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL);
266 mach_port_t replyPort = MACH_PORT_NULL;
267
268 if (!allocator) allocator = __CFGetDefaultAllocator();
269 retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort);
270 if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = _CFUserNotificationSendRequest(allocator, sessionID, replyPort, token, timeout, flags, dictionary);
271 if (ERR_SUCCESS == retval) {
272 userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, CFUserNotificationGetTypeID(), sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL);
273 if (userNotification) {
274 userNotification->_replyPort = replyPort;
275 userNotification->_token = token;
276 userNotification->_timeout = timeout;
277 userNotification->_requestFlags = flags;
278 userNotification->_responseFlags = 0;
279 userNotification->_sessionID = NULL;
280 userNotification->_responseDictionary = NULL;
281 userNotification->_machPort = NULL;
282 userNotification->_callout = NULL;
283 if (sessionID) userNotification->_sessionID = CFStringCreateCopy(allocator, sessionID);
284 } else {
285 retval = unix_err(ENOMEM);
286 }
287 } else {
288 if (dictionary) CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey));
289 }
290 if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort);
291 if (error) *error = retval;
292 return userNotification;
293 }
294
295 static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFIndex size, void *info) {
296 CFUserNotificationRef userNotification = (CFUserNotificationRef)info;
297 mach_msg_base_t *msg = (mach_msg_base_t *)m;
298 CFOptionFlags responseFlags = msg->header.msgh_id;
299 if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
300 CFDataRef responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
301 if (responseData) {
302 userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
303 CFRelease(responseData);
304 }
305 }
306 CFMachPortInvalidate(userNotification->_machPort);
307 CFRelease(userNotification->_machPort);
308 userNotification->_machPort = NULL;
309 mach_port_destroy(mach_task_self(), userNotification->_replyPort);
310 userNotification->_replyPort = MACH_PORT_NULL;
311 userNotification->_callout(userNotification, responseFlags);
312 }
313
314 SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) {
315 CHECK_FOR_FORK();
316 SInt32 retval = ERR_SUCCESS;
317 mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0;
318 mach_msg_base_t *msg = NULL;
319 CFIndex size = MAX_STRING_COUNT * MAX_STRING_LENGTH;
320 CFDataRef responseData;
321
322 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
323 msg = (mach_msg_base_t *)CFAllocatorAllocate(CFGetAllocator(userNotification), size, 0);
324 if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)");
325 if (msg) {
326 memset(msg, 0, size);
327 msg->header.msgh_size = size;
328 if (msgtime > 0) {
329 retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL);
330 } else {
331 retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL);
332 }
333 if (ERR_SUCCESS == retval) {
334 if (responseFlags) *responseFlags = msg->header.msgh_id;
335 if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
336 responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
337 if (responseData) {
338 userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
339 CFRelease(responseData);
340 }
341 }
342 if (userNotification->_machPort) {
343 CFMachPortInvalidate(userNotification->_machPort);
344 CFRelease(userNotification->_machPort);
345 userNotification->_machPort = NULL;
346 }
347 mach_port_destroy(mach_task_self(), userNotification->_replyPort);
348 userNotification->_replyPort = MACH_PORT_NULL;
349 }
350 CFAllocatorDeallocate(CFGetAllocator(userNotification), msg);
351 } else {
352 retval = unix_err(ENOMEM);
353 }
354 }
355 return retval;
356 }
357
358 CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) {
359 CHECK_FOR_FORK();
360 CFStringRef retval = NULL;
361 CFTypeRef value = NULL;
362 if (userNotification && userNotification->_responseDictionary) {
363 value = CFDictionaryGetValue(userNotification->_responseDictionary, key);
364 if (CFGetTypeID(value) == CFStringGetTypeID()) {
365 if (0 == idx) retval = (CFStringRef)value;
366 } else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
367 if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx);
368 }
369 }
370 return retval;
371 }
372
373 CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) {
374 CHECK_FOR_FORK();
375 return userNotification ? userNotification->_responseDictionary : NULL;
376 }
377
378 SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) {
379 CHECK_FOR_FORK();
380 SInt32 retval = ERR_SUCCESS;
381 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, timeout, flags|kCFUserNotificationUpdateFlag, dictionary);
382 return retval;
383 }
384
385 SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) {
386 CHECK_FOR_FORK();
387 SInt32 retval = ERR_SUCCESS;
388 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, 0, kCFUserNotificationCancelFlag, NULL);
389 return retval;
390 }
391
392 CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) {
393 CHECK_FOR_FORK();
394 CFRunLoopSourceRef source = NULL;
395 if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) {
396 CFMachPortContext context = {0, userNotification, NULL, NULL, NULL};
397 userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, NULL);
398 }
399 if (userNotification && userNotification->_machPort) {
400 source = CFMachPortCreateRunLoopSource(allocator, userNotification->_machPort, order);
401 userNotification->_callout = callout;
402 }
403 return source;
404 }
405
406 SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) {
407 CHECK_FOR_FORK();
408 CFUserNotificationRef userNotification;
409 SInt32 retval = ERR_SUCCESS;
410 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
411 if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
412 if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
413 if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
414 if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
415 if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
416 if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
417 userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict);
418 if (userNotification) CFRelease(userNotification);
419 CFRelease(dict);
420 return retval;
421 }
422
423
424 CF_EXPORT SInt32 CFUserNotificationDisplayAlert(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle, CFStringRef alternateButtonTitle, CFStringRef otherButtonTitle, CFOptionFlags *responseFlags) {
425 CHECK_FOR_FORK();
426 CFUserNotificationRef userNotification;
427 SInt32 retval = ERR_SUCCESS;
428 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
429 if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
430 if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
431 if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
432 if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
433 if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
434 if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
435 if (alternateButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, alternateButtonTitle);
436 if (otherButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, otherButtonTitle);
437 userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict);
438 if (userNotification) {
439 retval = CFUserNotificationReceiveResponse(userNotification, timeout, responseFlags);
440 if (MACH_RCV_TIMED_OUT == retval) {
441 retval = CFUserNotificationCancel(userNotification);
442 if (responseFlags) *responseFlags = kCFUserNotificationCancelResponse;
443 }
444 CFRelease(userNotification);
445 }
446 CFRelease(dict);
447 return retval;
448 }
449
450 #undef MAX_STRING_LENGTH
451 #undef MAX_STRING_COUNT
452 #undef NOTIFICATION_PORT_NAME
453 #undef MESSAGE_TIMEOUT
454 #undef MAX_PORT_NAME_LENGTH
455 #undef NOTIFICATION_PORT_NAME_SUFFIX
456