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