/ CFUserNotification.c
CFUserNotification.c
1 /* 2 * Copyright (c) 2015 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 24 /* CFUserNotification.c 25 Copyright (c) 2000-2014, Apple Inc. All rights reserved. 26 Original Author: Doug Davidson 27 Responsibility: Kevin Perry 28 */ 29 30 #include <CoreFoundation/CFUserNotification.h> 31 #include <CoreFoundation/CFPropertyList.h> 32 #include <CoreFoundation/CFNumber.h> 33 #include <CoreFoundation/CFRunLoop.h> 34 #include "CFInternal.h" 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> 45 46 #define CFUserNotificationLog(alertHeader, alertMessage) CFLog(3, CFSTR("%@: %@"), alertHeader, alertMessage); 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") 77 CONST_STRING_DECL(kCFUserNotificationKeyboardTypesKey, "KeyboardTypes") 78 CONST_STRING_DECL(kCFUserNotificationAlertTopMostKey, "AlertTopMost") // boolean value 79 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); 99 CFStringAppendFormat(result, NULL, CFSTR("<CFUserNotification %p>"), cf); 100 return result; 101 } 102 103 #define MAX_STRING_LENGTH PATH_MAX 104 #define MAX_STRING_COUNT 16 105 #define MAX_PORT_NAME_LENGTH 63 106 #define NOTIFICATION_PORT_NAME_SUFFIX ".session." 107 #define MESSAGE_TIMEOUT 100 108 #if DEPLOYMENT_TARGET_MACOSX 109 #define NOTIFICATION_PORT_NAME "com.apple.UNCUserNotification" 110 #elif DEPLOYMENT_TARGET_EMBEDDED 111 #define NOTIFICATION_PORT_NAME "com.apple.SBUserNotification" 112 #else 113 #error Unknown or unspecified DEPLOYMENT_TARGET 114 #endif 115 116 117 static void __CFUserNotificationDeallocate(CFTypeRef cf); 118 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 CFTypeID CFUserNotificationGetTypeID(void) { 132 static dispatch_once_t initOnce; 133 dispatch_once(&initOnce, ^{ __kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass); }); 134 return __kCFUserNotificationTypeID; 135 } 136 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 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; 208 char namebuffer[MAX_PORT_NAME_LENGTH + 1]; 209 210 strlcpy(namebuffer, NOTIFICATION_PORT_NAME, sizeof(namebuffer)); 211 if (sessionID) { 212 char sessionid[MAX_PORT_NAME_LENGTH + 1]; 213 CFIndex len = MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX); 214 CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, (uint8_t *)sessionid, len, &size); 215 sessionid[len - 1] = '\0'; 216 strlcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX, sizeof(namebuffer)); 217 strlcat(namebuffer, sessionid, sizeof(namebuffer)); 218 } 219 220 retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort); 221 if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up2(bootstrapPort, namebuffer, &serverPort, 0, 0); 222 if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) { 223 modifiedDictionary = _CFUserNotificationModifiedDictionary(allocator, dictionary, token, itimeout, _CFProcessNameString()); 224 if (modifiedDictionary) { 225 data = CFPropertyListCreateData(allocator, modifiedDictionary, kCFPropertyListXMLFormat_v1_0, 0, NULL); 226 if (data) { 227 size = sizeof(mach_msg_base_t) + ((CFDataGetLength(data) + 3) & (~0x3)); 228 msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); 229 if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); 230 if (msg) { 231 memset(msg, 0, size); 232 msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort == MACH_PORT_NULL) ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE); 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)); 239 //CFShow(CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false)); 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); 241 CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); 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 257 static SInt32 _getNextToken() { 258 static uint16_t tokenCounter = 0; 259 SInt32 token = ((getpid() << 16) | (tokenCounter++)); 260 return token; 261 } 262 263 CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) { 264 CHECK_FOR_FORK(); 265 CFUserNotificationRef userNotification = NULL; 266 SInt32 retval = ERR_SUCCESS; 267 SInt32 token = _getNextToken(); 268 CFStringRef sessionID = (dictionary ? CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL); 269 mach_port_t replyPort = MACH_PORT_NULL; 270 271 if (!allocator) allocator = __CFGetDefaultAllocator(); 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) { 275 userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, CFUserNotificationGetTypeID(), sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL); 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; 302 if (msg->header.msgh_size > sizeof(mach_msg_base_t)) { 303 CFDataRef responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); 304 if (responseData) { 305 userNotification->_responseDictionary = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL, NULL); 306 CFRelease(responseData); 307 } 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) { 318 CHECK_FOR_FORK(); 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) { 326 msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); 327 if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); 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; 338 if (msg->header.msgh_size > sizeof(mach_msg_base_t)) { 339 responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); 340 if (responseData) { 341 userNotification->_responseDictionary = CFPropertyListCreateWithData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL, NULL); 342 CFRelease(responseData); 343 } 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 } 353 CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); 354 } else { 355 retval = unix_err(ENOMEM); 356 } 357 } 358 return retval; 359 } 360 361 CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) { 362 CHECK_FOR_FORK(); 363 CFStringRef retval = NULL; 364 CFTypeRef value = NULL; 365 if (userNotification && userNotification->_responseDictionary) { 366 value = CFDictionaryGetValue(userNotification->_responseDictionary, key); 367 if (CFGetTypeID(value) == CFStringGetTypeID()) { 368 if (0 == idx) retval = (CFStringRef)value; 369 } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { 370 if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx); 371 } 372 } 373 return retval; 374 } 375 376 CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) { 377 CHECK_FOR_FORK(); 378 return userNotification ? userNotification->_responseDictionary : NULL; 379 } 380 381 SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { 382 CHECK_FOR_FORK(); 383 SInt32 retval = ERR_SUCCESS; 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 } 388 return retval; 389 } 390 391 SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { 392 CHECK_FOR_FORK(); 393 SInt32 retval = ERR_SUCCESS; 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 } 398 return retval; 399 } 400 401 CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) { 402 CHECK_FOR_FORK(); 403 CFRunLoopSourceRef source = NULL; 404 if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) { 405 CFMachPortContext context = {0, userNotification, NULL, NULL, NULL}; 406 userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, NULL); 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) { 416 CHECK_FOR_FORK(); 417 SInt32 retval = ERR_SUCCESS; 418 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 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); 425 retval = _CFUserNotificationSendRequest(__CFGetDefaultAllocator(), NULL, MACH_PORT_NULL, _getNextToken(), timeout, flags, dict); 426 if (ERR_SUCCESS != retval) { 427 CFUserNotificationLog(alertHeader, alertMessage); 428 } 429 CFRelease(dict); 430 return retval; 431 } 432 433 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) { 435 CHECK_FOR_FORK(); 436 CFUserNotificationRef userNotification; 437 SInt32 retval = ERR_SUCCESS; 438 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 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); 447 userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict); 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 460 #undef MAX_STRING_LENGTH 461 #undef MAX_STRING_COUNT 462 #undef NOTIFICATION_PORT_NAME 463 #undef MESSAGE_TIMEOUT 464 #undef MAX_PORT_NAME_LENGTH 465 #undef NOTIFICATION_PORT_NAME_SUFFIX 466