1/*
2 * Copyright (c) 2014 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-2013, 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
48enum {
49    kCFUserNotificationCancelFlag = (1 << 3),
50    kCFUserNotificationUpdateFlag = (1 << 4)
51};
52
53CONST_STRING_DECL(kCFUserNotificationTokenKey, "Token")
54CONST_STRING_DECL(kCFUserNotificationTimeoutKey, "Timeout")
55CONST_STRING_DECL(kCFUserNotificationFlagsKey, "Flags")
56CONST_STRING_DECL(kCFUserNotificationIconPathKey, "IconPath")
57CONST_STRING_DECL(kCFUserNotificationSoundPathKey, "SoundPath")
58CONST_STRING_DECL(kCFUserNotificationLocalizationPathKey, "LocalizationPath")
59CONST_STRING_DECL(kCFUserNotificationAlertSourceKey, "AlertSource")
60CONST_STRING_DECL(kCFUserNotificationTextFieldLabelsKey, "TextFieldTitles")
61CONST_STRING_DECL(kCFUserNotificationCheckBoxLabelsKey, "CheckBoxTitles")
62CONST_STRING_DECL(kCFUserNotificationIconURLKey, "IconURL")
63CONST_STRING_DECL(kCFUserNotificationSoundURLKey, "SoundURL")
64CONST_STRING_DECL(kCFUserNotificationLocalizationURLKey, "LocalizationURL")
65CONST_STRING_DECL(kCFUserNotificationAlertHeaderKey, "AlertHeader")
66CONST_STRING_DECL(kCFUserNotificationAlertMessageKey, "AlertMessage")
67CONST_STRING_DECL(kCFUserNotificationDefaultButtonTitleKey, "DefaultButtonTitle")
68CONST_STRING_DECL(kCFUserNotificationAlternateButtonTitleKey, "AlternateButtonTitle")
69CONST_STRING_DECL(kCFUserNotificationOtherButtonTitleKey, "OtherButtonTitle")
70CONST_STRING_DECL(kCFUserNotificationProgressIndicatorValueKey, "ProgressIndicatorValue")
71CONST_STRING_DECL(kCFUserNotificationSessionIDKey, "SessionID")
72CONST_STRING_DECL(kCFUserNotificationPopUpTitlesKey, "PopUpTitles")
73CONST_STRING_DECL(kCFUserNotificationTextFieldTitlesKey, "TextFieldTitles")
74CONST_STRING_DECL(kCFUserNotificationCheckBoxTitlesKey, "CheckBoxTitles")
75CONST_STRING_DECL(kCFUserNotificationTextFieldValuesKey, "TextFieldValues")
76CONST_STRING_DECL(kCFUserNotificationPopUpSelectionKey, "PopUpSelection")
77CONST_STRING_DECL(kCFUserNotificationKeyboardTypesKey, "KeyboardTypes")
78CONST_STRING_DECL(kCFUserNotificationAlertTopMostKey, "AlertTopMost") // boolean value
79
80
81static CFTypeID __kCFUserNotificationTypeID = _kCFRuntimeNotATypeID;
82
83struct __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
96static 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
117static void __CFUserNotificationDeallocate(CFTypeRef cf);
118
119static 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
131CF_PRIVATE void __CFUserNotificationInitialize(void) {
132    __kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass);
133}
134
135CFTypeID CFUserNotificationGetTypeID(void) {
136    if (_kCFRuntimeNotATypeID == __kCFUserNotificationTypeID) __CFUserNotificationInitialize();
137    return __kCFUserNotificationTypeID;
138}
139
140static 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
152static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) {
153    if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
154}
155
156static 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
204static 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;
211    char namebuffer[MAX_PORT_NAME_LENGTH + 1];
212
213    strlcpy(namebuffer, NOTIFICATION_PORT_NAME, sizeof(namebuffer));
214    if (sessionID) {
215        char sessionid[MAX_PORT_NAME_LENGTH + 1];
216        CFIndex len = MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX);
217        CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, (uint8_t *)sessionid, len, &size);
218        sessionid[len - 1] = '\0';
219        strlcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX, sizeof(namebuffer));
220        strlcat(namebuffer, sessionid, sizeof(namebuffer));
221    }
222
223    retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort);
224    if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up2(bootstrapPort, namebuffer, &serverPort, 0, 0);
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));
231                msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
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, (replyPort == MACH_PORT_NULL) ? 0 : 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));
242                    //CFShow(CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false));
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);
244                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg);
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
260static SInt32 _getNextToken() {
261    static uint16_t tokenCounter = 0;
262    SInt32 token = ((getpid() << 16) | (tokenCounter++));
263    return token;
264}
265
266CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) {
267    CHECK_FOR_FORK();
268    CFUserNotificationRef userNotification = NULL;
269    SInt32 retval = ERR_SUCCESS;
270    SInt32 token = _getNextToken();
271    CFStringRef sessionID = (dictionary ? CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL);
272    mach_port_t replyPort = MACH_PORT_NULL;
273
274    if (!allocator) allocator = __CFGetDefaultAllocator();
275    retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort);
276    if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = _CFUserNotificationSendRequest(allocator, sessionID, replyPort, token, timeout, flags, dictionary);
277    if (ERR_SUCCESS == retval) {
278        userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, CFUserNotificationGetTypeID(), sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL);
279        if (userNotification) {
280            userNotification->_replyPort = replyPort;
281            userNotification->_token = token;
282            userNotification->_timeout = timeout;
283            userNotification->_requestFlags = flags;
284            userNotification->_responseFlags = 0;
285            userNotification->_sessionID = NULL;
286            userNotification->_responseDictionary = NULL;
287            userNotification->_machPort = NULL;
288            userNotification->_callout = NULL;
289            if (sessionID) userNotification->_sessionID = CFStringCreateCopy(allocator, sessionID);
290        } else {
291            retval = unix_err(ENOMEM);
292        }
293    } else {
294        if (dictionary) CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey));
295    }
296    if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort);
297    if (error) *error = retval;
298    return userNotification;
299}
300
301static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFIndex size, void *info) {
302    CFUserNotificationRef userNotification = (CFUserNotificationRef)info;
303    mach_msg_base_t *msg = (mach_msg_base_t *)m;
304    CFOptionFlags responseFlags = msg->header.msgh_id;
305    if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
306        CFDataRef responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
307        if (responseData) {
308            userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
309            CFRelease(responseData);
310        }
311    }
312    CFMachPortInvalidate(userNotification->_machPort);
313    CFRelease(userNotification->_machPort);
314    userNotification->_machPort = NULL;
315    mach_port_destroy(mach_task_self(), userNotification->_replyPort);
316    userNotification->_replyPort = MACH_PORT_NULL;
317    userNotification->_callout(userNotification, responseFlags);
318}
319
320SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) {
321    CHECK_FOR_FORK();
322    SInt32 retval = ERR_SUCCESS;
323    mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0;
324    mach_msg_base_t *msg = NULL;
325    CFIndex size = MAX_STRING_COUNT * MAX_STRING_LENGTH;
326    CFDataRef responseData;
327
328    if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
329        msg = (mach_msg_base_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
330        if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)");
331        if (msg) {
332            memset(msg, 0, size);
333            msg->header.msgh_size = size;
334            if (msgtime > 0) {
335                retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL);
336            } else {
337                retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL);
338            }
339            if (ERR_SUCCESS == retval) {
340                if (responseFlags) *responseFlags = msg->header.msgh_id;
341                if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
342                    responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
343                    if (responseData) {
344                        userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
345                        CFRelease(responseData);
346                    }
347                }
348                if (userNotification->_machPort) {
349                    CFMachPortInvalidate(userNotification->_machPort);
350                    CFRelease(userNotification->_machPort);
351                    userNotification->_machPort = NULL;
352                }
353                mach_port_destroy(mach_task_self(), userNotification->_replyPort);
354                userNotification->_replyPort = MACH_PORT_NULL;
355            }
356            CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg);
357        } else {
358            retval = unix_err(ENOMEM);
359        }
360    }
361    return retval;
362}
363
364CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) {
365    CHECK_FOR_FORK();
366    CFStringRef retval = NULL;
367    CFTypeRef value = NULL;
368    if (userNotification && userNotification->_responseDictionary) {
369        value = CFDictionaryGetValue(userNotification->_responseDictionary, key);
370        if (CFGetTypeID(value) == CFStringGetTypeID()) {
371            if (0 == idx) retval = (CFStringRef)value;
372        } else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
373            if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx);
374        }
375    }
376    return retval;
377}
378
379CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) {
380    CHECK_FOR_FORK();
381    return userNotification ? userNotification->_responseDictionary : NULL;
382}
383
384SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) {
385    CHECK_FOR_FORK();
386    SInt32 retval = ERR_SUCCESS;
387    if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
388        // 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.
389        retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, MACH_PORT_NULL, userNotification->_token, timeout, flags|kCFUserNotificationUpdateFlag, dictionary);
390    }
391    return retval;
392}
393
394SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) {
395    CHECK_FOR_FORK();
396    SInt32 retval = ERR_SUCCESS;
397    if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
398        // 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.
399        retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, MACH_PORT_NULL, userNotification->_token, 0, kCFUserNotificationCancelFlag, NULL);
400    }
401    return retval;
402}
403
404CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) {
405    CHECK_FOR_FORK();
406    CFRunLoopSourceRef source = NULL;
407    if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) {
408        CFMachPortContext context = {0, userNotification, NULL, NULL, NULL};
409        userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, NULL);
410    }
411    if (userNotification && userNotification->_machPort) {
412        source = CFMachPortCreateRunLoopSource(allocator, userNotification->_machPort, order);
413        userNotification->_callout = callout;
414    }
415    return source;
416}
417
418SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) {
419    CHECK_FOR_FORK();
420    SInt32 retval = ERR_SUCCESS;
421    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
422    if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
423    if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
424    if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
425    if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
426    if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
427    if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
428    retval = _CFUserNotificationSendRequest(__CFGetDefaultAllocator(), NULL, MACH_PORT_NULL, _getNextToken(), timeout, flags, dict);
429    if (ERR_SUCCESS != retval) {
430        CFUserNotificationLog(alertHeader, alertMessage);
431    }
432    CFRelease(dict);
433    return retval;
434}
435
436
437CF_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) {
438    CHECK_FOR_FORK();
439    CFUserNotificationRef userNotification;
440    SInt32 retval = ERR_SUCCESS;
441    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
442    if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
443    if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
444    if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
445    if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
446    if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
447    if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
448    if (alternateButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, alternateButtonTitle);
449    if (otherButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, otherButtonTitle);
450    userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict);
451    if (userNotification) {
452        retval = CFUserNotificationReceiveResponse(userNotification, timeout, responseFlags);
453        if (MACH_RCV_TIMED_OUT == retval) {
454            retval = CFUserNotificationCancel(userNotification);
455            if (responseFlags) *responseFlags = kCFUserNotificationCancelResponse;
456        }
457        CFRelease(userNotification);
458    }
459    CFRelease(dict);
460    return retval;
461}
462
463#undef MAX_STRING_LENGTH
464#undef MAX_STRING_COUNT
465#undef NOTIFICATION_PORT_NAME
466#undef MESSAGE_TIMEOUT
467#undef MAX_PORT_NAME_LENGTH
468#undef NOTIFICATION_PORT_NAME_SUFFIX
469
470