1/* UNCUserNotification.c 2 Copyright 2000, Apple Computer, Inc. All rights reserved. 3*/ 4 5#if 0 6 7#include "UNCUserNotification.h" 8#include <stdlib.h> 9#include <unistd.h> 10#include <stdio.h> 11#include <mach/mach.h> 12#include <mach/error.h> 13#include <servers/bootstrap.h> 14#include <limits.h> 15#include <errno.h> 16 17#define MAX_STRING_LENGTH PATH_MAX 18#define MAX_STRING_COUNT 16 19#define MAX_PORT_NAME_LENGTH 63 20#define NOTIFICATION_PORT_NAME "com.apple.UNCUserNotification" 21#define NOTIFICATION_PORT_NAME_OLD "UNCUserNotification" 22#define NOTIFICATION_PORT_NAME_SUFFIX ".session." 23#define MESSAGE_TIMEOUT 100 24 25enum { 26 kUNCCancelFlag = (1 << 3), 27 kUNCUpdateFlag = (1 << 4) 28}; 29 30/* backward compatibility */ 31extern const char kUNCTextFieldLabelsKey[]; 32extern const char kUNCCheckBoxLabelsKey[]; 33 34/* forward compatibility */ 35extern const char kUNCSessionIDKey[]; 36 37const char kUNCTokenKey[] = "Token"; 38const char kUNCTimeoutKey[] = "Timeout"; 39const char kUNCAlertSourceKey[] = "AlertSource"; 40const char kUNCIconPathKey[] = "IconPath"; 41const char kUNCSoundPathKey[] = "SoundPath"; 42const char kUNCLocalizationPathKey[] = "LocalizationPath"; 43const char kUNCAlertHeaderKey[] = "AlertHeader"; 44const char kUNCAlertMessageKey[] = "AlertMessage"; 45const char kUNCDefaultButtonTitleKey[] = "DefaultButtonTitle"; 46const char kUNCAlternateButtonTitleKey[] = "AlternateButtonTitle"; 47const char kUNCOtherButtonTitleKey[] = "OtherButtonTitle"; 48const char kUNCProgressIndicatorValueKey[] = "ProgressIndicatorValue"; 49const char kUNCSessionIDKey[] = "SessionID"; 50const char kUNCPopUpTitlesKey[] = "PopUpTitles"; 51const char kUNCTextFieldTitlesKey[] = "TextFieldTitles"; 52const char kUNCTextFieldLabelsKey[] = "TextFieldTitles"; 53const char kUNCCheckBoxTitlesKey[] = "CheckBoxTitles"; 54const char kUNCCheckBoxLabelsKey[] = "CheckBoxTitles"; 55const char kUNCTextFieldValuesKey[] = "TextFieldValues"; 56const char kUNCPopUpSelectionKey[] = "PopUpSelection"; 57 58static const char *kUNCXMLPrologue = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; 59static const char *kUNCDoctypePrologue = "<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">"; 60static const char *kUNCPlistPrologue = "<plist version=\"0.9\">"; 61static const char *kUNCDictionaryPrologue = "<dict>"; 62static const char *kUNCDictionaryEpilogue = "</dict>"; 63static const char *kUNCArrayPrologue = "<array>"; 64static const char *kUNCArrayEpilogue = "</array>"; 65static const char *kUNCKeyPrologue = "<key>"; 66static const char *kUNCKeyEpilogue = "</key>"; 67static const char *kUNCStringPrologue = "<string>"; 68static const char *kUNCStringEpilogue = "</string>"; 69static const char *kUNCIntegerPrologue = "<integer>"; 70static const char *kUNCIntegerEpilogue = "</integer>"; 71static const char *kUNCPlistEpilogue = "</plist>"; 72 73struct __UNCUserNotification { 74 mach_port_t _replyPort; 75 int _token; 76 double _timeout; 77 unsigned long _requestFlags; 78 unsigned long _responseFlags; 79 char *_sessionID; 80 mach_msg_base_t *_response; 81 char **_responseContents; 82}; 83 84static unsigned long UNCPackContents(char *buffer, const char **contents, int token, int itimeout, char *source) { 85 // if buffer is non-null, write XML into it; if buffer is null, return required size 86 // should consider escape sequences 87 unsigned long keyLen, valLen; 88 const char **p = contents, *key = NULL, *val = NULL, *nextKey = NULL, *previousKey = NULL; 89 char *b = buffer, tokenString[64], timeoutString[64]; 90 91 snprintf(tokenString, sizeof(tokenString)-1, "%d", token); tokenString[sizeof(tokenString)-1] = '\0'; 92 snprintf(timeoutString, sizeof(timeoutString)-1, "%d", itimeout); timeoutString[sizeof(timeoutString)-1] = '\0'; 93#define APPEND(x) {if (buffer) strcpy(b, x); b += strlen(x);} 94#define APPENDN(x, n) {if (buffer) strncpy(b, x, n); b += n;} 95 APPEND(kUNCXMLPrologue); APPEND("\n"); 96 APPEND(kUNCDoctypePrologue); APPEND("\n"); 97 APPEND(kUNCPlistPrologue); APPEND("\n"); 98 APPEND(kUNCDictionaryPrologue); APPEND("\n"); 99 APPEND("\t"); APPEND(kUNCKeyPrologue); APPEND(kUNCTokenKey); APPEND(kUNCKeyEpilogue); APPEND("\n"); 100 APPEND("\t"); APPEND(kUNCIntegerPrologue); APPEND(tokenString); APPEND(kUNCIntegerEpilogue); APPEND("\n"); 101 APPEND("\t"); APPEND(kUNCKeyPrologue); APPEND(kUNCTimeoutKey); APPEND(kUNCKeyEpilogue); APPEND("\n"); 102 APPEND("\t"); APPEND(kUNCIntegerPrologue); APPEND(timeoutString); APPEND(kUNCIntegerEpilogue); APPEND("\n"); 103 APPEND("\t"); APPEND(kUNCKeyPrologue); APPEND(kUNCAlertSourceKey); APPEND(kUNCKeyEpilogue); APPEND("\n"); 104 APPEND("\t"); APPEND(kUNCStringPrologue); APPEND(source); APPEND(kUNCStringEpilogue); APPEND("\n"); 105 106 while (p && *p) { 107 key = *p++; val = *p++; 108 if (val) { 109 nextKey = *p; 110 keyLen = strlen(key); if (keyLen > MAX_STRING_LENGTH) keyLen = MAX_STRING_LENGTH; 111 valLen = strlen(val); if (valLen > MAX_STRING_LENGTH) valLen = MAX_STRING_LENGTH; 112 if (key != previousKey) { 113 APPEND("\t"); APPEND(kUNCKeyPrologue); 114 APPENDN(key, keyLen); 115 APPEND(kUNCKeyEpilogue); APPEND("\n"); 116 } 117 if ((key != previousKey) && (key == nextKey)) { 118 APPEND("\t"); APPEND(kUNCArrayPrologue); APPEND("\n"); 119 } 120 if ((key == previousKey) || (key == nextKey)) APPEND("\t"); 121 APPEND("\t"); APPEND(kUNCStringPrologue); 122 APPENDN(val, valLen); 123 APPEND(kUNCStringEpilogue); APPEND("\n"); 124 if ((key == previousKey) && (key != nextKey)) { 125 APPEND("\t"); APPEND(kUNCArrayEpilogue); APPEND("\n"); 126 } 127 previousKey = key; 128 } 129 } 130 131 APPEND(kUNCDictionaryEpilogue); APPEND("\n"); 132 APPEND(kUNCPlistEpilogue); APPEND("\n"); 133#undef APPEND 134#undef APPENDN 135 136 return b - buffer; 137} 138 139static void convertEscapes(char *str) { 140 char *p = str, *q = str; 141 for (p = q = str; 0 != (*q = *p); p++, q++) { 142 if ('&' == *p) { 143 if ('g' == *(p+1) && 't' == *(p+2) && ';' == *(p+3)) { 144 *q = '>'; 145 p += 3; 146 } else if ('l' == *(p+1) && 't' == *(p+2) && ';' == *(p+3)) { 147 *q = '<'; 148 p += 3; 149 } else if ('a' == *(p+1) && 'm' == *(p+2) && 'p' == *(p+3) && ';' == *(p+4)) { 150 *q = '&'; 151 p += 4; 152 } 153 } 154 } 155} 156 157static unsigned long UNCUnpackContents(char *buffer, char **contents) { 158 // if contents is non-null, unpack XML buffer into it; if contents is null, return required size 159 // if contents is non-null, as side effect, insert null string terminators in buffer and convert some escapes in place 160 char **p = contents, *key = NULL, *keyEnd = NULL, *previousKey = NULL, *value = NULL, *valueEnd = NULL, *b = buffer; 161 162 while (b) { 163 key = strstr(b, kUNCKeyPrologue); if (key) key += strlen(kUNCKeyPrologue); 164 value = strstr(b, kUNCStringPrologue); if (value) value += strlen(kUNCStringPrologue); 165 b = NULL; 166 if (key || (previousKey && value)) { 167 if (contents) *p = NULL; 168 if (0 != key && key < value) { 169 b = key; 170 keyEnd = strstr(b, kUNCKeyEpilogue); 171 if (keyEnd) { 172 if (contents) { 173 *p = key; 174 *keyEnd = '\0'; 175 } 176 previousKey = key; 177 b = keyEnd + strlen(kUNCKeyEpilogue); 178 value = strstr(b, kUNCStringPrologue); if (value) value += strlen(kUNCStringPrologue); 179 } 180 } else { 181 if (contents) *p = previousKey; 182 } 183 p++; 184 185 if (contents) *p = NULL; 186 if (0 != value) { 187 b = value; 188 valueEnd = strstr(b, kUNCStringEpilogue); 189 if (valueEnd) { 190 if (contents) { 191 *p = value; 192 *valueEnd = '\0'; 193 convertEscapes(value); 194 } 195 b = valueEnd + strlen(kUNCStringEpilogue); 196 } 197 } 198 p++; 199 } 200 } 201 if (p > contents) { 202 if (contents) *p = NULL; 203 p++; 204 } 205 206 return p - contents; 207} 208 209static const char *UNCSessionIDForContents(const char **contents) { 210 const char **p = contents, *retval = NULL; 211 212 while (!retval && p && *p) { 213 if (0 == strcmp(*p++, kUNCSessionIDKey)) retval = *p; 214 p++; 215 } 216 return retval; 217} 218 219extern char ***_NSGetArgv(void); 220 221static int UNCSendRequest(const char *sessionID, mach_port_t replyPort, int token, double timeout, unsigned long flags, const char **contents) { 222 int retval = ERR_SUCCESS, itimeout = (timeout > 0.0 && timeout < INT_MAX) ? (int)timeout : 0; 223 mach_msg_base_t *msg = NULL; 224 mach_port_t bootstrapPort = MACH_PORT_NULL, serverPort = MACH_PORT_NULL; 225 unsigned long size; 226 char namebuffer[MAX_PORT_NAME_LENGTH + 1], oldnamebuffer[MAX_PORT_NAME_LENGTH + 1], *source = (*_NSGetArgv())[0], *p = source; 227 228 strcpy(namebuffer, NOTIFICATION_PORT_NAME); 229 strcpy(oldnamebuffer, NOTIFICATION_PORT_NAME_OLD); 230 if (sessionID) { 231 strcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX); 232 strncat(namebuffer, sessionID, MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX)); 233 namebuffer[MAX_PORT_NAME_LENGTH] = '\0'; 234 235 strcat(oldnamebuffer, NOTIFICATION_PORT_NAME_SUFFIX); 236 strncat(oldnamebuffer, sessionID, MAX_PORT_NAME_LENGTH - sizeof(NOTIFICATION_PORT_NAME_OLD) - sizeof(NOTIFICATION_PORT_NAME_SUFFIX)); 237 oldnamebuffer[MAX_PORT_NAME_LENGTH] = '\0'; 238 } 239 240 retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort); 241 if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up(bootstrapPort, namebuffer, &serverPort); 242 if (ERR_SUCCESS != retval || MACH_PORT_NULL == serverPort) retval = bootstrap_look_up(bootstrapPort, oldnamebuffer, &serverPort); 243 if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) { 244 while (*p) if ('/' == *p++) source = p; 245 size = sizeof(mach_msg_base_t) + ((UNCPackContents(NULL, contents, token, itimeout, source) + 3) & (~0x3)); 246 msg = (mach_msg_base_t *)malloc(size); 247 if (msg) { 248 bzero(msg, size); 249 msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); 250 msg->header.msgh_size = size; 251 msg->header.msgh_remote_port = serverPort; 252 msg->header.msgh_local_port = replyPort; 253 msg->header.msgh_id = flags; 254 msg->body.msgh_descriptor_count = 0; 255 UNCPackContents((char *)msg + sizeof(mach_msg_base_t), contents, token, itimeout, source); 256 retval = mach_msg((mach_msg_header_t *)msg, MACH_SEND_MSG|MACH_SEND_TIMEOUT, size, 0, MACH_PORT_NULL, MESSAGE_TIMEOUT, MACH_PORT_NULL); 257 free(msg); 258 } else { 259 retval = unix_err(ENOMEM); 260 } 261 } 262 return retval; 263} 264 265extern UNCUserNotificationRef UNCUserNotificationCreate(double timeout, unsigned long flags, int *error, const char **contents) { 266 UNCUserNotificationRef userNotification = NULL; 267 int retval = ERR_SUCCESS; 268 static unsigned short tokenCounter = 0; 269 int token = ((getpid()<<16) | (tokenCounter++)); 270 const char *sessionID = UNCSessionIDForContents(contents); 271 mach_port_t replyPort = MACH_PORT_NULL; 272 size_t idlen; 273 274 retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort); 275 if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = UNCSendRequest(sessionID, replyPort, token, timeout, flags, contents); 276 if (ERR_SUCCESS == retval) { 277 userNotification = (UNCUserNotificationRef)malloc(sizeof(struct __UNCUserNotification)); 278 if (userNotification) { 279 bzero(userNotification, sizeof(struct __UNCUserNotification)); 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->_response = NULL; 287 userNotification->_responseContents = NULL; 288 if (sessionID) { 289 idlen = strlen(sessionID); 290 if (idlen > MAX_PORT_NAME_LENGTH) idlen = MAX_PORT_NAME_LENGTH; 291 userNotification->_sessionID = (char *)malloc(idlen + 1); 292 strncpy(userNotification->_sessionID, sessionID, idlen); 293 userNotification->_sessionID[idlen] = '\0'; 294 } 295 } else { 296 retval = unix_err(ENOMEM); 297 } 298 } 299 if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort); 300 if (error) *error = retval; 301 return userNotification; 302} 303 304extern int UNCUserNotificationReceiveResponse(UNCUserNotificationRef userNotification, double timeout, unsigned long *responseFlags) { 305 int retval = ERR_SUCCESS; 306 mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0; 307 mach_msg_base_t *msg = NULL; 308 unsigned long size = MAX_STRING_COUNT * MAX_STRING_LENGTH, contentSize = 0; 309 310 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { 311 msg = (mach_msg_base_t *)malloc(size); 312 if (msg) { 313 bzero(msg, size); 314 msg->header.msgh_size = size; 315 if (msgtime > 0) { 316 retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL); 317 } else { 318 retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL); 319 } 320 if (ERR_SUCCESS == retval) { 321 if (responseFlags) *responseFlags = msg->header.msgh_id; 322 userNotification->_response = msg; 323 contentSize = UNCUnpackContents((char *)msg + sizeof(mach_msg_base_t), NULL); 324 if (0 < contentSize) { 325 userNotification->_responseContents = (char **)malloc(contentSize * sizeof(char **)); 326 if (userNotification->_responseContents) { 327 UNCUnpackContents((char *)msg + sizeof(mach_msg_base_t), userNotification->_responseContents); 328 } 329 } 330 mach_port_destroy(mach_task_self(), userNotification->_replyPort); 331 userNotification->_replyPort = MACH_PORT_NULL; 332 } else { 333 free(msg); 334 } 335 } else { 336 retval = unix_err(ENOMEM); 337 } 338 } 339 return retval; 340} 341 342extern const char *UNCUserNotificationGetResponseValue(UNCUserNotificationRef userNotification, const char *key, unsigned long index) { 343 char **p, *retval = NULL; 344 if (userNotification && userNotification->_responseContents && key) { 345 p = userNotification->_responseContents; 346 while (!retval && *p) { 347 if (0 == strcmp(*p++, key) && 0 == index--) retval = *p; 348 p++; 349 } 350 } 351 return retval; 352} 353 354extern const char **UNCUserNotificationGetResponseContents(UNCUserNotificationRef userNotification) { 355 return userNotification ? (const char **)(userNotification->_responseContents) : NULL; 356} 357 358extern int UNCUserNotificationUpdate(UNCUserNotificationRef userNotification, double timeout, unsigned long flags, const char **contents) { 359 int retval = ERR_SUCCESS; 360 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { 361 retval = UNCSendRequest(userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, timeout, flags|kUNCUpdateFlag, contents); 362 } 363 return retval; 364} 365 366extern int UNCUserNotificationCancel(UNCUserNotificationRef userNotification) { 367 int retval = ERR_SUCCESS; 368 if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { 369 retval = UNCSendRequest(userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, 0, kUNCCancelFlag, NULL); 370 } 371 return retval; 372} 373 374extern void UNCUserNotificationFree(UNCUserNotificationRef userNotification) { 375 if (userNotification) { 376 if (MACH_PORT_NULL != userNotification->_replyPort) mach_port_destroy(mach_task_self(), userNotification->_replyPort); 377 if (userNotification->_sessionID) free(userNotification->_sessionID); 378 if (userNotification->_responseContents) free(userNotification->_responseContents); 379 if (userNotification->_response) free(userNotification->_response); 380 free(userNotification); 381 } 382} 383 384extern int UNCDisplayNotice(double timeout, unsigned long flags, const char *iconPath, const char *soundPath, const char *localizationPath, const char *alertHeader, const char *alertMessage, const char *defaultButtonTitle) { 385 UNCUserNotificationRef userNotification; 386 int retval = ERR_SUCCESS; 387 const char *contents[13]; 388 unsigned long i = 0; 389 if (iconPath) {contents[i++] = kUNCIconPathKey; contents[i++] = iconPath;} 390 if (soundPath) {contents[i++] = kUNCSoundPathKey; contents[i++] = soundPath;} 391 if (localizationPath) {contents[i++] = kUNCLocalizationPathKey; contents[i++] = localizationPath;} 392 if (alertHeader) {contents[i++] = kUNCAlertHeaderKey; contents[i++] = alertHeader;} 393 if (alertMessage) {contents[i++] = kUNCAlertMessageKey; contents[i++] = alertMessage;} 394 if (defaultButtonTitle) {contents[i++] = kUNCDefaultButtonTitleKey; contents[i++] = defaultButtonTitle;} 395 contents[i++] = NULL; 396 userNotification = UNCUserNotificationCreate(timeout, flags, &retval, contents); 397 if (userNotification) UNCUserNotificationFree(userNotification); 398 return retval; 399} 400 401extern int UNCDisplayAlert(double timeout, unsigned long flags, const char *iconPath, const char *soundPath, const char *localizationPath, const char *alertHeader, const char *alertMessage, const char *defaultButtonTitle, const char *alternateButtonTitle, const char *otherButtonTitle, unsigned long *responseFlags) { 402 UNCUserNotificationRef userNotification; 403 int retval = ERR_SUCCESS; 404 const char *contents[17]; 405 unsigned long i = 0; 406 if (iconPath) {contents[i++] = kUNCIconPathKey; contents[i++] = iconPath;} 407 if (soundPath) {contents[i++] = kUNCSoundPathKey; contents[i++] = soundPath;} 408 if (localizationPath) {contents[i++] = kUNCLocalizationPathKey; contents[i++] = localizationPath;} 409 if (alertHeader) {contents[i++] = kUNCAlertHeaderKey; contents[i++] = alertHeader;} 410 if (alertMessage) {contents[i++] = kUNCAlertMessageKey; contents[i++] = alertMessage;} 411 if (defaultButtonTitle) {contents[i++] = kUNCDefaultButtonTitleKey; contents[i++] = defaultButtonTitle;} 412 if (alternateButtonTitle) {contents[i++] = kUNCAlternateButtonTitleKey; contents[i++] = alternateButtonTitle;} 413 if (otherButtonTitle) {contents[i++] = kUNCOtherButtonTitleKey; contents[i++] = otherButtonTitle;} 414 contents[i++] = NULL; 415 userNotification = UNCUserNotificationCreate(timeout, flags, &retval, contents); 416 if (userNotification) { 417 retval = UNCUserNotificationReceiveResponse(userNotification, timeout, responseFlags); 418 UNCUserNotificationFree(userNotification); 419 } 420 return retval; 421} 422 423#endif 424 425