]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
d8b101a4 | 2 | * Copyright (c) 2014 Apple Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d7384798 | 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. | |
d7384798 | 12 | * |
9ce05555 A |
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. | |
d7384798 | 20 | * |
9ce05555 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
f64f9b69 | 23 | |
9ce05555 | 24 | /* CFUserNotification.c |
d7384798 | 25 | Copyright (c) 2000-2014, Apple Inc. All rights reserved. |
8ca704e1 A |
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 | ||
9ce05555 | 131 | CFTypeID CFUserNotificationGetTypeID(void) { |
d7384798 A |
132 | static dispatch_once_t initOnce; |
133 | dispatch_once(&initOnce, ^{ __kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass); }); | |
9ce05555 A |
134 | return __kCFUserNotificationTypeID; |
135 | } | |
136 | ||
d8925383 A |
137 | static void __CFUserNotificationDeallocate(CFTypeRef cf) { |
138 | CFUserNotificationRef userNotification = (CFUserNotificationRef)cf; | |
139 | if (userNotification->_machPort) { | |
140 | CFMachPortInvalidate(userNotification->_machPort); | |
141 | CFRelease(userNotification->_machPort); | |
142 | } else if (MACH_PORT_NULL != userNotification->_replyPort) { | |
143 | mach_port_destroy(mach_task_self(), userNotification->_replyPort); | |
144 | } | |
145 | if (userNotification->_sessionID) CFRelease(userNotification->_sessionID); | |
146 | if (userNotification->_responseDictionary) CFRelease(userNotification->_responseDictionary); | |
147 | } | |
148 | ||
9ce05555 A |
149 | static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) { |
150 | if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value); | |
151 | } | |
152 | ||
153 | static CFDictionaryRef _CFUserNotificationModifiedDictionary(CFAllocatorRef allocator, CFDictionaryRef dictionary, SInt32 token, SInt32 timeout, CFStringRef source) { | |
154 | CFMutableDictionaryRef md = CFDictionaryCreateMutable(allocator, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
155 | CFNumberRef tokenNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &token); | |
156 | CFNumberRef timeoutNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &timeout); | |
157 | CFURLRef url = NULL; | |
158 | CFStringRef path = NULL; | |
159 | ||
160 | if (dictionary) CFDictionaryApplyFunction(dictionary, _CFUserNotificationAddToDictionary, md); | |
161 | if (source) CFDictionaryAddValue(md, kCFUserNotificationAlertSourceKey, source); | |
162 | if (tokenNumber) { | |
163 | CFDictionaryAddValue(md, kCFUserNotificationTokenKey, tokenNumber); | |
164 | CFRelease(tokenNumber); | |
165 | } | |
166 | if (timeoutNumber) { | |
167 | CFDictionaryAddValue(md, kCFUserNotificationTimeoutKey, timeoutNumber); | |
168 | CFRelease(timeoutNumber); | |
169 | } | |
170 | ||
171 | url = CFDictionaryGetValue(md, kCFUserNotificationIconURLKey); | |
172 | if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { | |
173 | url = CFURLCopyAbsoluteURL(url); | |
174 | CFDictionaryRemoveValue(md, kCFUserNotificationIconURLKey); | |
175 | path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); | |
176 | CFDictionaryAddValue(md, kCFUserNotificationIconPathKey, path); | |
177 | CFRelease(url); | |
178 | CFRelease(path); | |
179 | } | |
180 | url = CFDictionaryGetValue(md, kCFUserNotificationSoundURLKey); | |
181 | if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { | |
182 | url = CFURLCopyAbsoluteURL(url); | |
183 | CFDictionaryRemoveValue(md, kCFUserNotificationSoundURLKey); | |
184 | path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); | |
185 | CFDictionaryAddValue(md, kCFUserNotificationSoundPathKey, path); | |
186 | CFRelease(url); | |
187 | CFRelease(path); | |
188 | } | |
189 | url = CFDictionaryGetValue(md, kCFUserNotificationLocalizationURLKey); | |
190 | if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { | |
191 | url = CFURLCopyAbsoluteURL(url); | |
192 | CFDictionaryRemoveValue(md, kCFUserNotificationLocalizationURLKey); | |
193 | path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); | |
194 | CFDictionaryAddValue(md, kCFUserNotificationLocalizationPathKey, path); | |
195 | CFRelease(url); | |
196 | CFRelease(path); | |
197 | } | |
198 | return md; | |
199 | } | |
200 | ||
201 | static SInt32 _CFUserNotificationSendRequest(CFAllocatorRef allocator, CFStringRef sessionID, mach_port_t replyPort, SInt32 token, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { | |
202 | CFDictionaryRef modifiedDictionary = NULL; | |
203 | SInt32 retval = ERR_SUCCESS, itimeout = (timeout > 0.0 && timeout < INT_MAX) ? (SInt32)timeout : 0; | |
204 | CFDataRef data; | |
205 | mach_msg_base_t *msg = NULL; | |
206 | mach_port_t bootstrapPort = MACH_PORT_NULL, serverPort = MACH_PORT_NULL; | |
207 | CFIndex size; | |
bd5b749c | 208 | char namebuffer[MAX_PORT_NAME_LENGTH + 1]; |
cf7d2af9 | 209 | |
bd5b749c | 210 | strlcpy(namebuffer, NOTIFICATION_PORT_NAME, sizeof(namebuffer)); |
9ce05555 | 211 | if (sessionID) { |
cf7d2af9 A |
212 | char sessionid[MAX_PORT_NAME_LENGTH + 1]; |
213 | CFIndex len = MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX); | |
bd5b749c | 214 | CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, (uint8_t *)sessionid, len, &size); |
cf7d2af9 A |
215 | sessionid[len - 1] = '\0'; |
216 | strlcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX, sizeof(namebuffer)); | |
217 | strlcat(namebuffer, sessionid, sizeof(namebuffer)); | |
9ce05555 A |
218 | } |
219 | ||
220 | retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort); | |
bd5b749c | 221 | if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up2(bootstrapPort, namebuffer, &serverPort, 0, 0); |
9ce05555 A |
222 | if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) { |
223 | modifiedDictionary = _CFUserNotificationModifiedDictionary(allocator, dictionary, token, itimeout, _CFProcessNameString()); | |
224 | if (modifiedDictionary) { | |
d7384798 | 225 | data = CFPropertyListCreateData(allocator, modifiedDictionary, kCFPropertyListXMLFormat_v1_0, 0, NULL); |
9ce05555 A |
226 | if (data) { |
227 | size = sizeof(mach_msg_base_t) + ((CFDataGetLength(data) + 3) & (~0x3)); | |
8ca704e1 | 228 | msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); |
9ce05555 A |
229 | if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); |
230 | if (msg) { | |
231 | memset(msg, 0, size); | |
856091c5 | 232 | msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort == MACH_PORT_NULL) ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE); |
9ce05555 A |
233 | msg->header.msgh_size = size; |
234 | msg->header.msgh_remote_port = serverPort; | |
235 | msg->header.msgh_local_port = replyPort; | |
236 | msg->header.msgh_id = flags; | |
237 | msg->body.msgh_descriptor_count = 0; | |
238 | CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (uint8_t *)msg + sizeof(mach_msg_base_t)); | |
bd5b749c | 239 | //CFShow(CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false)); |
9ce05555 | 240 | 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 | 241 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); |
9ce05555 A |
242 | } else { |
243 | retval = unix_err(ENOMEM); | |
244 | } | |
245 | CFRelease(data); | |
246 | } else { | |
247 | retval = unix_err(ENOMEM); | |
248 | } | |
249 | CFRelease(modifiedDictionary); | |
250 | } else { | |
251 | retval = unix_err(ENOMEM); | |
252 | } | |
253 | } | |
254 | return retval; | |
255 | } | |
256 | ||
a48904a4 A |
257 | static SInt32 _getNextToken() { |
258 | static uint16_t tokenCounter = 0; | |
259 | SInt32 token = ((getpid() << 16) | (tokenCounter++)); | |
260 | return token; | |
261 | } | |
262 | ||
9ce05555 | 263 | CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) { |
bd5b749c | 264 | CHECK_FOR_FORK(); |
9ce05555 A |
265 | CFUserNotificationRef userNotification = NULL; |
266 | SInt32 retval = ERR_SUCCESS; | |
a48904a4 | 267 | SInt32 token = _getNextToken(); |
9ce05555 A |
268 | CFStringRef sessionID = (dictionary ? CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL); |
269 | mach_port_t replyPort = MACH_PORT_NULL; | |
270 | ||
271 | if (!allocator) allocator = __CFGetDefaultAllocator(); | |
9ce05555 A |
272 | retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort); |
273 | if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = _CFUserNotificationSendRequest(allocator, sessionID, replyPort, token, timeout, flags, dictionary); | |
274 | if (ERR_SUCCESS == retval) { | |
bd5b749c | 275 | userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, CFUserNotificationGetTypeID(), sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL); |
9ce05555 A |
276 | if (userNotification) { |
277 | userNotification->_replyPort = replyPort; | |
278 | userNotification->_token = token; | |
279 | userNotification->_timeout = timeout; | |
280 | userNotification->_requestFlags = flags; | |
281 | userNotification->_responseFlags = 0; | |
282 | userNotification->_sessionID = NULL; | |
283 | userNotification->_responseDictionary = NULL; | |
284 | userNotification->_machPort = NULL; | |
285 | userNotification->_callout = NULL; | |
286 | if (sessionID) userNotification->_sessionID = CFStringCreateCopy(allocator, sessionID); | |
287 | } else { | |
288 | retval = unix_err(ENOMEM); | |
289 | } | |
290 | } else { | |
291 | if (dictionary) CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey)); | |
292 | } | |
293 | if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort); | |
294 | if (error) *error = retval; | |
295 | return userNotification; | |
296 | } | |
297 | ||
298 | static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFIndex size, void *info) { | |
299 | CFUserNotificationRef userNotification = (CFUserNotificationRef)info; | |
300 | mach_msg_base_t *msg = (mach_msg_base_t *)m; | |
301 | CFOptionFlags responseFlags = msg->header.msgh_id; | |
d8925383 | 302 | if (msg->header.msgh_size > sizeof(mach_msg_base_t)) { |
bd5b749c | 303 | CFDataRef responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); |
d8925383 | 304 | if (responseData) { |
d7384798 | 305 | userNotification->_responseDictionary = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL, NULL); |
d8925383 A |
306 | CFRelease(responseData); |
307 | } | |
9ce05555 A |
308 | } |
309 | CFMachPortInvalidate(userNotification->_machPort); | |
310 | CFRelease(userNotification->_machPort); | |
311 | userNotification->_machPort = NULL; | |
312 | mach_port_destroy(mach_task_self(), userNotification->_replyPort); | |
313 | userNotification->_replyPort = MACH_PORT_NULL; | |
314 | userNotification->_callout(userNotification, responseFlags); | |
315 | } | |
316 | ||
317 | SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) { | |
bd5b749c | 318 | CHECK_FOR_FORK(); |
9ce05555 A |
319 | SInt32 retval = ERR_SUCCESS; |
320 | mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0; | |
321 | mach_msg_base_t *msg = NULL; | |
322 | CFIndex size = MAX_STRING_COUNT * MAX_STRING_LENGTH; | |
323 | CFDataRef responseData; | |
324 | ||
325 | if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { | |
8ca704e1 | 326 | msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); |
cf7d2af9 | 327 | if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); |
9ce05555 A |
328 | if (msg) { |
329 | memset(msg, 0, size); | |
330 | msg->header.msgh_size = size; | |
331 | if (msgtime > 0) { | |
332 | retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL); | |
333 | } else { | |
334 | retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL); | |
335 | } | |
336 | if (ERR_SUCCESS == retval) { | |
337 | if (responseFlags) *responseFlags = msg->header.msgh_id; | |
d8925383 | 338 | if (msg->header.msgh_size > sizeof(mach_msg_base_t)) { |
bd5b749c | 339 | responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); |
d8925383 | 340 | if (responseData) { |
d7384798 | 341 | userNotification->_responseDictionary = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL, NULL); |
d8925383 A |
342 | CFRelease(responseData); |
343 | } | |
9ce05555 A |
344 | } |
345 | if (userNotification->_machPort) { | |
346 | CFMachPortInvalidate(userNotification->_machPort); | |
347 | CFRelease(userNotification->_machPort); | |
348 | userNotification->_machPort = NULL; | |
349 | } | |
350 | mach_port_destroy(mach_task_self(), userNotification->_replyPort); | |
351 | userNotification->_replyPort = MACH_PORT_NULL; | |
352 | } | |
8ca704e1 | 353 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); |
9ce05555 A |
354 | } else { |
355 | retval = unix_err(ENOMEM); | |
356 | } | |
357 | } | |
358 | return retval; | |
359 | } | |
360 | ||
361 | CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) { | |
bd5b749c | 362 | CHECK_FOR_FORK(); |
9ce05555 A |
363 | CFStringRef retval = NULL; |
364 | CFTypeRef value = NULL; | |
365 | if (userNotification && userNotification->_responseDictionary) { | |
366 | value = CFDictionaryGetValue(userNotification->_responseDictionary, key); | |
367 | if (CFGetTypeID(value) == CFStringGetTypeID()) { | |
cf7d2af9 | 368 | if (0 == idx) retval = (CFStringRef)value; |
9ce05555 | 369 | } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { |
cf7d2af9 | 370 | if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx); |
9ce05555 A |
371 | } |
372 | } | |
373 | return retval; | |
374 | } | |
375 | ||
376 | CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) { | |
bd5b749c | 377 | CHECK_FOR_FORK(); |
9ce05555 A |
378 | return userNotification ? userNotification->_responseDictionary : NULL; |
379 | } | |
380 | ||
381 | SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { | |
bd5b749c | 382 | CHECK_FOR_FORK(); |
9ce05555 | 383 | SInt32 retval = ERR_SUCCESS; |
856091c5 A |
384 | if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { |
385 | // Avoid including a new send-once right with update/cancel messages by passing MACH_PORT_NULL, since the server doesn't need to use them. | |
386 | retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, MACH_PORT_NULL, userNotification->_token, timeout, flags|kCFUserNotificationUpdateFlag, dictionary); | |
387 | } | |
9ce05555 A |
388 | return retval; |
389 | } | |
390 | ||
391 | SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { | |
bd5b749c | 392 | CHECK_FOR_FORK(); |
9ce05555 | 393 | SInt32 retval = ERR_SUCCESS; |
856091c5 A |
394 | if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { |
395 | // Avoid including a new send-once right with update/cancel messages by passing MACH_PORT_NULL, since the server doesn't need to use them. | |
396 | retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, MACH_PORT_NULL, userNotification->_token, 0, kCFUserNotificationCancelFlag, NULL); | |
397 | } | |
9ce05555 A |
398 | return retval; |
399 | } | |
400 | ||
401 | CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) { | |
bd5b749c | 402 | CHECK_FOR_FORK(); |
9ce05555 A |
403 | CFRunLoopSourceRef source = NULL; |
404 | if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) { | |
405 | CFMachPortContext context = {0, userNotification, NULL, NULL, NULL}; | |
cf7d2af9 | 406 | userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, NULL); |
9ce05555 A |
407 | } |
408 | if (userNotification && userNotification->_machPort) { | |
409 | source = CFMachPortCreateRunLoopSource(allocator, userNotification->_machPort, order); | |
410 | userNotification->_callout = callout; | |
411 | } | |
412 | return source; | |
413 | } | |
414 | ||
415 | SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) { | |
bd5b749c | 416 | CHECK_FOR_FORK(); |
9ce05555 | 417 | SInt32 retval = ERR_SUCCESS; |
bd5b749c | 418 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
9ce05555 A |
419 | if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL); |
420 | if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL); | |
421 | if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL); | |
422 | if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader); | |
423 | if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage); | |
424 | if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle); | |
a48904a4 A |
425 | retval = _CFUserNotificationSendRequest(__CFGetDefaultAllocator(), NULL, MACH_PORT_NULL, _getNextToken(), timeout, flags, dict); |
426 | if (ERR_SUCCESS != retval) { | |
427 | CFUserNotificationLog(alertHeader, alertMessage); | |
428 | } | |
9ce05555 A |
429 | CFRelease(dict); |
430 | return retval; | |
431 | } | |
432 | ||
bd5b749c | 433 | |
9ce05555 | 434 | 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 | 435 | CHECK_FOR_FORK(); |
9ce05555 A |
436 | CFUserNotificationRef userNotification; |
437 | SInt32 retval = ERR_SUCCESS; | |
bd5b749c | 438 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
9ce05555 A |
439 | if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL); |
440 | if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL); | |
441 | if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL); | |
442 | if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader); | |
443 | if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage); | |
444 | if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle); | |
445 | if (alternateButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, alternateButtonTitle); | |
446 | if (otherButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, otherButtonTitle); | |
bd5b749c | 447 | userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict); |
9ce05555 A |
448 | if (userNotification) { |
449 | retval = CFUserNotificationReceiveResponse(userNotification, timeout, responseFlags); | |
450 | if (MACH_RCV_TIMED_OUT == retval) { | |
451 | retval = CFUserNotificationCancel(userNotification); | |
452 | if (responseFlags) *responseFlags = kCFUserNotificationCancelResponse; | |
453 | } | |
454 | CFRelease(userNotification); | |
455 | } | |
456 | CFRelease(dict); | |
457 | return retval; | |
458 | } | |
459 | ||
9ce05555 A |
460 | #undef MAX_STRING_LENGTH |
461 | #undef MAX_STRING_COUNT | |
462 | #undef NOTIFICATION_PORT_NAME | |
9ce05555 | 463 | #undef MESSAGE_TIMEOUT |
bd5b749c A |
464 | #undef MAX_PORT_NAME_LENGTH |
465 | #undef NOTIFICATION_PORT_NAME_SUFFIX | |
9ce05555 | 466 |