/ 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