blob: 7e45cd4e5bf5d3e076fd3a40c019aacc92c87ca3 [file] [log] [blame]
/*
* Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved.
*
* This source code is a modified version of the CoreFoundation sources released by Apple Inc. under
* the terms of the APSL version 2.0 (see below).
*
* For information about changes from the original Apple source release can be found by reviewing the
* source control system for the project at https://sourceforge.net/svn/?group_id=246198.
*
* The original license information is as follows:
*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* CFUserNotification.c
Copyright (c) 2000-2007 Apple Inc. All rights reserved.
Responsibility: Doug Davidson
*/
#include <CoreFoundation/CFUserNotification.h>
#include <CoreFoundation/CFPropertyList.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFRunLoop.h>
#include "CFInternal.h"
#include <CoreFoundation/CFMachPort.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#if DEPLOYMENT_TARGET_WINDOWS
#include <process.h>
#define getpid _getpid
#define ERR_SUCCESS 0
#define MACH_RCV_TIMED_OUT 0
#else
#include <unistd.h>
#endif
#if DEPLOYMENT_TARGET_MACOSX
#include <mach/mach.h>
#include <mach/error.h>
#include <bootstrap_priv.h>
#include <pthread.h>
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
#define CFUserNotificationLog(alertHeader, alertMessage) CFLog(3, CFSTR("%@: %@"), alertHeader, alertMessage);
enum {
kCFUserNotificationCancelFlag = (1 << 3),
kCFUserNotificationUpdateFlag = (1 << 4)
};
CONST_STRING_DECL(kCFUserNotificationTokenKey, "Token")
CONST_STRING_DECL(kCFUserNotificationTimeoutKey, "Timeout")
CONST_STRING_DECL(kCFUserNotificationFlagsKey, "Flags")
CONST_STRING_DECL(kCFUserNotificationIconPathKey, "IconPath")
CONST_STRING_DECL(kCFUserNotificationSoundPathKey, "SoundPath")
CONST_STRING_DECL(kCFUserNotificationLocalizationPathKey, "LocalizationPath")
CONST_STRING_DECL(kCFUserNotificationAlertSourceKey, "AlertSource")
CONST_STRING_DECL(kCFUserNotificationTextFieldLabelsKey, "TextFieldTitles")
CONST_STRING_DECL(kCFUserNotificationCheckBoxLabelsKey, "CheckBoxTitles")
CONST_STRING_DECL(kCFUserNotificationIconURLKey, "IconURL")
CONST_STRING_DECL(kCFUserNotificationSoundURLKey, "SoundURL")
CONST_STRING_DECL(kCFUserNotificationLocalizationURLKey, "LocalizationURL")
CONST_STRING_DECL(kCFUserNotificationAlertHeaderKey, "AlertHeader")
CONST_STRING_DECL(kCFUserNotificationAlertMessageKey, "AlertMessage")
CONST_STRING_DECL(kCFUserNotificationDefaultButtonTitleKey, "DefaultButtonTitle")
CONST_STRING_DECL(kCFUserNotificationAlternateButtonTitleKey, "AlternateButtonTitle")
CONST_STRING_DECL(kCFUserNotificationOtherButtonTitleKey, "OtherButtonTitle")
CONST_STRING_DECL(kCFUserNotificationProgressIndicatorValueKey, "ProgressIndicatorValue")
CONST_STRING_DECL(kCFUserNotificationSessionIDKey, "SessionID")
CONST_STRING_DECL(kCFUserNotificationPopUpTitlesKey, "PopUpTitles")
CONST_STRING_DECL(kCFUserNotificationTextFieldTitlesKey, "TextFieldTitles")
CONST_STRING_DECL(kCFUserNotificationCheckBoxTitlesKey, "CheckBoxTitles")
CONST_STRING_DECL(kCFUserNotificationTextFieldValuesKey, "TextFieldValues")
CONST_STRING_DECL(kCFUserNotificationPopUpSelectionKey, "PopUpSelection")
CONST_STRING_DECL(kCFUserNotificationKeyboardTypesKey, "KeyboardTypes")
static CFTypeID __kCFUserNotificationTypeID = _kCFRuntimeNotATypeID;
struct __CFUserNotification {
CFRuntimeBase _base;
SInt32 _replyPort;
SInt32 _token;
CFTimeInterval _timeout;
CFOptionFlags _requestFlags;
CFOptionFlags _responseFlags;
CFStringRef _sessionID;
CFDictionaryRef _responseDictionary;
#if DEPLOYMENT_TARGET_MACOSX
CFMachPortRef _machPort;
#endif
CFUserNotificationCallBack _callout;
};
static CFStringRef __CFUserNotificationCopyDescription(CFTypeRef cf) {
CFMutableStringRef result;
result = CFStringCreateMutable(CFGetAllocator(cf), 0);
CFStringAppendFormat(result, NULL, CFSTR("<CFUserNotification %p>"), cf);
return result;
}
#define MAX_STRING_LENGTH PATH_MAX
#define MAX_STRING_COUNT 16
#define MAX_PORT_NAME_LENGTH 63
#define NOTIFICATION_PORT_NAME_SUFFIX ".session."
#define MESSAGE_TIMEOUT 100
#if DEPLOYMENT_TARGET_MACOSX
#define NOTIFICATION_PORT_NAME "com.apple.UNCUserNotification"
#elif DEPLOYMENT_TARGET_WINDOWS
#define NOTIFICATION_PORT_NAME "com.apple.SBUserNotification"
#else
#error Unknown or unspecified DEPLOYMENT_TARGET
#endif
static void __CFUserNotificationDeallocate(CFTypeRef cf);
static const CFRuntimeClass __CFUserNotificationClass = {
0,
"CFUserNotification",
NULL, // init
NULL, // copy
__CFUserNotificationDeallocate,
NULL, // equal
NULL, // hash
NULL, //
__CFUserNotificationCopyDescription
};
__private_extern__ void __CFUserNotificationInitialize(void) {
__kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass);
}
CFTypeID CFUserNotificationGetTypeID(void) {
if (_kCFRuntimeNotATypeID == __kCFUserNotificationTypeID) __CFUserNotificationInitialize();
return __kCFUserNotificationTypeID;
}
static void __CFUserNotificationDeallocate(CFTypeRef cf) {
CFUserNotificationRef userNotification = (CFUserNotificationRef)cf;
#if DEPLOYMENT_TARGET_MACOSX
if (userNotification->_machPort) {
CFMachPortInvalidate(userNotification->_machPort);
CFRelease(userNotification->_machPort);
} else if (MACH_PORT_NULL != userNotification->_replyPort) {
mach_port_destroy(mach_task_self(), userNotification->_replyPort);
}
#endif
if (userNotification->_sessionID) CFRelease(userNotification->_sessionID);
if (userNotification->_responseDictionary) CFRelease(userNotification->_responseDictionary);
}
static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) {
if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
}
static CFDictionaryRef _CFUserNotificationModifiedDictionary(CFAllocatorRef allocator, CFDictionaryRef dictionary, SInt32 token, SInt32 timeout, CFStringRef source) {
CFMutableDictionaryRef md = CFDictionaryCreateMutable(allocator, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFNumberRef tokenNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &token);
CFNumberRef timeoutNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &timeout);
CFURLRef url = NULL;
CFStringRef path = NULL;
if (dictionary) CFDictionaryApplyFunction(dictionary, _CFUserNotificationAddToDictionary, md);
if (source) CFDictionaryAddValue(md, kCFUserNotificationAlertSourceKey, source);
if (tokenNumber) {
CFDictionaryAddValue(md, kCFUserNotificationTokenKey, tokenNumber);
CFRelease(tokenNumber);
}
if (timeoutNumber) {
CFDictionaryAddValue(md, kCFUserNotificationTimeoutKey, timeoutNumber);
CFRelease(timeoutNumber);
}
url = (CFURLRef)CFDictionaryGetValue(md, kCFUserNotificationIconURLKey);
if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
url = CFURLCopyAbsoluteURL(url);
CFDictionaryRemoveValue(md, kCFUserNotificationIconURLKey);
path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
CFDictionaryAddValue(md, kCFUserNotificationIconPathKey, path);
CFRelease(url);
CFRelease(path);
}
url = (CFURLRef)CFDictionaryGetValue(md, kCFUserNotificationSoundURLKey);
if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
url = CFURLCopyAbsoluteURL(url);
CFDictionaryRemoveValue(md, kCFUserNotificationSoundURLKey);
path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
CFDictionaryAddValue(md, kCFUserNotificationSoundPathKey, path);
CFRelease(url);
CFRelease(path);
}
url = (CFURLRef)CFDictionaryGetValue(md, kCFUserNotificationLocalizationURLKey);
if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) {
url = CFURLCopyAbsoluteURL(url);
CFDictionaryRemoveValue(md, kCFUserNotificationLocalizationURLKey);
path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
CFDictionaryAddValue(md, kCFUserNotificationLocalizationPathKey, path);
CFRelease(url);
CFRelease(path);
}
return md;
}
#if DEPLOYMENT_TARGET_MACOSX
static SInt32 _CFUserNotificationSendRequest(CFAllocatorRef allocator, CFStringRef sessionID, mach_port_t replyPort, SInt32 token, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) {
CFDictionaryRef modifiedDictionary = NULL;
SInt32 retval = ERR_SUCCESS, itimeout = (timeout > 0.0 && timeout < INT_MAX) ? (SInt32)timeout : 0;
CFDataRef data;
mach_msg_base_t *msg = NULL;
mach_port_t bootstrapPort = MACH_PORT_NULL, serverPort = MACH_PORT_NULL;
CFIndex size;
char namebuffer[MAX_PORT_NAME_LENGTH + 1];
strlcpy(namebuffer, NOTIFICATION_PORT_NAME, sizeof(namebuffer));
if (sessionID) {
char sessionid[MAX_PORT_NAME_LENGTH + 1];
CFIndex len = MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX);
CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, (uint8_t *)sessionid, len, &size);
sessionid[len - 1] = '\0';
strlcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX, sizeof(namebuffer));
strlcat(namebuffer, sessionid, sizeof(namebuffer));
}
retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort);
if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up2(bootstrapPort, namebuffer, &serverPort, 0, 0);
if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) {
modifiedDictionary = _CFUserNotificationModifiedDictionary(allocator, dictionary, token, itimeout, _CFProcessNameString());
if (modifiedDictionary) {
data = CFPropertyListCreateXMLData(allocator, modifiedDictionary);
if (data) {
size = sizeof(mach_msg_base_t) + ((CFDataGetLength(data) + 3) & (~0x3));
msg = (mach_msg_base_t *)CFAllocatorAllocate(allocator, size, 0);
if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)");
if (msg) {
memset(msg, 0, size);
msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
msg->header.msgh_size = size;
msg->header.msgh_remote_port = serverPort;
msg->header.msgh_local_port = replyPort;
msg->header.msgh_id = flags;
msg->body.msgh_descriptor_count = 0;
CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (uint8_t *)msg + sizeof(mach_msg_base_t));
//CFShow(CFStringCreateWithBytes(kCFAllocatorSystemDefault, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false));
retval = mach_msg((mach_msg_header_t *)msg, MACH_SEND_MSG|MACH_SEND_TIMEOUT, size, 0, MACH_PORT_NULL, MESSAGE_TIMEOUT, MACH_PORT_NULL);
CFAllocatorDeallocate(allocator, msg);
} else {
retval = unix_err(ENOMEM);
}
CFRelease(data);
} else {
retval = unix_err(ENOMEM);
}
CFRelease(modifiedDictionary);
} else {
retval = unix_err(ENOMEM);
}
}
return retval;
}
#endif
CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) {
CHECK_FOR_FORK();
CFUserNotificationRef userNotification = NULL;
SInt32 retval = ERR_SUCCESS;
static uint16_t tokenCounter = 0;
SInt32 token = ((getpid()<<16) | (tokenCounter++));
CFStringRef sessionID = (dictionary ? (CFStringRef)CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL);
#if DEPLOYMENT_TARGET_MACOSX
mach_port_t replyPort = MACH_PORT_NULL;
if (!allocator) allocator = __CFGetDefaultAllocator();
retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort);
if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = _CFUserNotificationSendRequest(allocator, sessionID, replyPort, token, timeout, flags, dictionary);
#else
SInt32 replyPort = 0;
#endif
if (ERR_SUCCESS == retval) {
userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, CFUserNotificationGetTypeID(), sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL);
if (userNotification) {
userNotification->_replyPort = replyPort;
userNotification->_token = token;
userNotification->_timeout = timeout;
userNotification->_requestFlags = flags;
userNotification->_responseFlags = 0;
userNotification->_sessionID = NULL;
userNotification->_responseDictionary = NULL;
#if DEPLOYMENT_TARGET_MACOSX
userNotification->_machPort = NULL;
#endif
userNotification->_callout = NULL;
if (sessionID) userNotification->_sessionID = CFStringCreateCopy(allocator, sessionID);
} else {
#if DEPLOYMENT_TARGET_MACOSX
retval = unix_err(ENOMEM);
#else
retval = -1;
#endif
}
} else {
if (dictionary) CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey));
}
#if DEPLOYMENT_TARGET_MACOSX
if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort);
#endif
if (error) *error = retval;
return userNotification;
}
#if DEPLOYMENT_TARGET_MACOSX
static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFIndex size, void *info) {
CFUserNotificationRef userNotification = (CFUserNotificationRef)info;
mach_msg_base_t *msg = (mach_msg_base_t *)m;
CFOptionFlags responseFlags = msg->header.msgh_id;
if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
CFDataRef responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
if (responseData) {
userNotification->_responseDictionary = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
CFRelease(responseData);
}
}
CFMachPortInvalidate(userNotification->_machPort);
CFRelease(userNotification->_machPort);
userNotification->_machPort = NULL;
mach_port_destroy(mach_task_self(), userNotification->_replyPort);
userNotification->_replyPort = MACH_PORT_NULL;
userNotification->_callout(userNotification, responseFlags);
}
#endif
SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) {
CHECK_FOR_FORK();
SInt32 retval = ERR_SUCCESS;
#if DEPLOYMENT_TARGET_MACOSX
mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0;
mach_msg_base_t *msg = NULL;
CFIndex size = MAX_STRING_COUNT * MAX_STRING_LENGTH;
CFDataRef responseData;
if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
msg = (mach_msg_base_t *)CFAllocatorAllocate(CFGetAllocator(userNotification), size, 0);
if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)");
if (msg) {
memset(msg, 0, size);
msg->header.msgh_size = size;
if (msgtime > 0) {
retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL);
} else {
retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL);
}
if (ERR_SUCCESS == retval) {
if (responseFlags) *responseFlags = msg->header.msgh_id;
if (msg->header.msgh_size > sizeof(mach_msg_base_t)) {
responseData = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t));
if (responseData) {
userNotification->_responseDictionary = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, responseData, kCFPropertyListImmutable, NULL);
CFRelease(responseData);
}
}
if (userNotification->_machPort) {
CFMachPortInvalidate(userNotification->_machPort);
CFRelease(userNotification->_machPort);
userNotification->_machPort = NULL;
}
mach_port_destroy(mach_task_self(), userNotification->_replyPort);
userNotification->_replyPort = MACH_PORT_NULL;
}
CFAllocatorDeallocate(CFGetAllocator(userNotification), msg);
} else {
retval = unix_err(ENOMEM);
}
}
#endif
return retval;
}
CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) {
CHECK_FOR_FORK();
CFStringRef retval = NULL;
CFTypeRef value = NULL;
if (userNotification && userNotification->_responseDictionary) {
value = CFDictionaryGetValue(userNotification->_responseDictionary, key);
if (CFGetTypeID(value) == CFStringGetTypeID()) {
if (0 == idx) {
retval = (CFStringRef)value;
}
} else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) {
retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx);
}
}
}
return retval;
}
CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) {
CHECK_FOR_FORK();
return userNotification ? userNotification->_responseDictionary : NULL;
}
SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) {
CHECK_FOR_FORK();
SInt32 retval = ERR_SUCCESS;
#if DEPLOYMENT_TARGET_MACOSX
if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, timeout, flags|kCFUserNotificationUpdateFlag, dictionary);
}
#endif
return retval;
}
SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) {
CHECK_FOR_FORK();
SInt32 retval = ERR_SUCCESS;
#if DEPLOYMENT_TARGET_MACOSX
if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) {
retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, 0, kCFUserNotificationCancelFlag, NULL);
}
#endif
return retval;
}
CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) {
CHECK_FOR_FORK();
CFRunLoopSourceRef source = NULL;
#if DEPLOYMENT_TARGET_MACOSX
if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) {
CFMachPortContext context = {0, userNotification, NULL, NULL, NULL};
userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, false);
}
if (userNotification && userNotification->_machPort) {
source = CFMachPortCreateRunLoopSource(allocator, userNotification->_machPort, order);
userNotification->_callout = callout;
}
#endif
return source;
}
SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) {
CHECK_FOR_FORK();
CFUserNotificationRef userNotification;
SInt32 retval = ERR_SUCCESS;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict);
if (userNotification) CFRelease(userNotification);
CFRelease(dict);
return retval;
}
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) {
CHECK_FOR_FORK();
CFUserNotificationRef userNotification;
SInt32 retval = ERR_SUCCESS;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL);
if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL);
if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader);
if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage);
if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle);
if (alternateButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, alternateButtonTitle);
if (otherButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, otherButtonTitle);
userNotification = CFUserNotificationCreate(kCFAllocatorSystemDefault, timeout, flags, &retval, dict);
if (userNotification) {
retval = CFUserNotificationReceiveResponse(userNotification, timeout, responseFlags);
if (MACH_RCV_TIMED_OUT == retval) {
retval = CFUserNotificationCancel(userNotification);
if (responseFlags) *responseFlags = kCFUserNotificationCancelResponse;
}
CFRelease(userNotification);
}
CFRelease(dict);
return retval;
}
#undef MAX_STRING_LENGTH
#undef MAX_STRING_COUNT
#undef NOTIFICATION_PORT_NAME
#undef MESSAGE_TIMEOUT
#undef MAX_PORT_NAME_LENGTH
#undef NOTIFICATION_PORT_NAME_SUFFIX
#endif /* DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS */