add growl agent for macos
authorDerrick Brashear <shadow@dementia.org>
Thu, 4 Mar 2010 21:07:14 +0000 (16:07 -0500)
committerDerrick Brashear <shadow@dementia.org>
Fri, 5 Mar 2010 03:52:36 +0000 (19:52 -0800)
a userspace monitoring helper for mac users

Change-Id: If81c0b9d2c7606e81f271465491f0a0eb5db62b3
Reviewed-on: http://gerrit.openafs.org/1513
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

12 files changed:
configure.in
src/platform/DARWIN/Makefile.in
src/platform/DARWIN/growlagent/._growlagent-openafs [new file with mode: 0755]
src/platform/DARWIN/growlagent/.gitignore [new file with mode: 0644]
src/platform/DARWIN/growlagent/CFGrowlAdditions.c [new file with mode: 0644]
src/platform/DARWIN/growlagent/CFGrowlAdditions.h [new file with mode: 0644]
src/platform/DARWIN/growlagent/CFGrowlDefines.h [new file with mode: 0644]
src/platform/DARWIN/growlagent/GrowlDefines.h [new file with mode: 0644]
src/platform/DARWIN/growlagent/GrowlDefinesInternal.h [new file with mode: 0644]
src/platform/DARWIN/growlagent/GrowlPathway.h [new file with mode: 0644]
src/platform/DARWIN/growlagent/Makefile.in [new file with mode: 0644]
src/platform/DARWIN/growlagent/main.m [new file with mode: 0644]

index b061a8d..7ecc364 100644 (file)
@@ -117,6 +117,7 @@ src/packaging/RedHat/openafs.spec \
 src/pam/Makefile \
 src/platform/Makefile \
 src/platform/${MKAFS_OSTYPE}/Makefile \
+src/platform/DARWIN/growlagent/Makefile \
 src/procmgmt/Makefile \
 src/procmgmt/test/Makefile \
 src/ptserver/Makefile \
index 96fe1bd..c52d4fe 100644 (file)
@@ -14,7 +14,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 
 .PHONY: all afscell
 
-all: OpenAFS.prefPane afssettings afscell
+all: OpenAFS.prefPane afssettings afscell growlagent
 
 AFSPreference/build/Release/OpenAFS.prefPane: OpenAFS.prefPane
 afscell/build/Release/afscell.bundle: afscell
@@ -32,6 +32,9 @@ OpenAFS.prefPane:
                echo Not building AFS prefPane for ${SYS_NAME} ;; \
        esac
 
+growlagent:
+       cd growlagent && $(MAKE) all
+
 afscell:
        xcodebuild -project ${srcdir}/afscell/afscell.xcodeproj SYMROOT=`pwd`/afscell/build
 
@@ -40,12 +43,14 @@ afssettings: afssettings.m
 
 install: \
        ${DESTDIR}${sbindir}/afssettings
+       cd growlagent && $(MAKE) install
 
 dest: \
        ${DEST}/etc/afssettings \
        ${DEST}/installer/afscell.bundle \
        ${DEST}/installer/InstallerSections.plist \
        ${DEST}/tools/OpenAFS.prefPane
+       cd growlagent && $(MAKE) dest
 
 ${DESTDIR}${sbindir}/afssettings: afssettings
        ${INSTALL} $? $@
@@ -71,3 +76,4 @@ clean:
        $(RM) -rf AFSPreference/build
        $(RM) -rf afscell/build
        $(RM) -f *.o core afssettings AFS_component_version_number.c
+       cd growlagent && $(MAKE) clean
diff --git a/src/platform/DARWIN/growlagent/._growlagent-openafs b/src/platform/DARWIN/growlagent/._growlagent-openafs
new file mode 100755 (executable)
index 0000000..a6a94dc
Binary files /dev/null and b/src/platform/DARWIN/growlagent/._growlagent-openafs differ
diff --git a/src/platform/DARWIN/growlagent/.gitignore b/src/platform/DARWIN/growlagent/.gitignore
new file mode 100644 (file)
index 0000000..b943e5a
--- /dev/null
@@ -0,0 +1,5 @@
+# After changing this file, please run
+#     git ls-files -i --exclude-standard
+# to check that you haven't inadvertently ignored any tracked files.
+!._growlagent-openafs
+/growlagent-openafs
diff --git a/src/platform/DARWIN/growlagent/CFGrowlAdditions.c b/src/platform/DARWIN/growlagent/CFGrowlAdditions.c
new file mode 100644 (file)
index 0000000..f8592ea
--- /dev/null
@@ -0,0 +1,629 @@
+//
+//  CFGrowlAdditions.c
+//  Growl
+//
+//  Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
+//  Copyright 2005-2006 The Growl Project.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#include <Carbon/Carbon.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "CFGrowlAdditions.h"
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+//extern Boolean CFStringGetFileSystemRepresentation(CFStringRef str) __attribute__((weak_import));
+extern CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) __attribute__((weak_import));
+
+char *createFileSystemRepresentationOfString(CFStringRef str) {
+       char *buffer;
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
+       /* CFStringGetFileSystemRepresentation will cause a link error despite the weak_import attribute above on 10.5 when compiling with 10.2 compatibility using gcc 3.3.
+        * PPC will therefore always use the 10.3 and below method of creating a file system representation.
+        */
+       if (1/*CFStringGetFileSystemRepresentation*/) {
+               CFIndex size = CFStringGetMaximumSizeOfFileSystemRepresentation(str);
+               buffer = malloc(size);
+               CFStringGetFileSystemRepresentation(str, buffer, size);
+       } else 
+#endif
+       {
+               buffer = malloc(512);
+               CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
+               if (!CFURLGetFileSystemRepresentation(url, false, (UInt8 *)buffer, 512)) {
+                       free(buffer);
+                       buffer = NULL;
+               }
+               CFRelease(url);
+       }
+       return buffer;
+}
+
+STRING_TYPE createStringWithDate(CFDateRef date) {
+       CFLocaleRef locale = CFLocaleCopyCurrent();
+       CFDateFormatterRef dateFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
+                                                                                                                        locale,
+                                                                                                                        kCFDateFormatterMediumStyle,
+                                                                                                                        kCFDateFormatterMediumStyle);
+       CFRelease(locale);
+       CFStringRef dateString = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
+                                                                                                                                dateFormatter,
+                                                                                                                                date);
+       CFRelease(dateFormatter);
+       return dateString;
+}
+
+STRING_TYPE createStringWithContentsOfFile(CFStringRef filename, CFStringEncoding encoding) {
+       CFStringRef str = NULL;
+
+       char *path = createFileSystemRepresentationOfString(filename);
+       if (path) {
+               FILE *fp = fopen(path, "rb");
+               if (fp) {
+                       fseek(fp, 0, SEEK_END);
+                       unsigned long size = ftell(fp);
+                       fseek(fp, 0, SEEK_SET);
+                       unsigned char *buffer = malloc(size);
+                       if (buffer && fread(buffer, 1, size, fp) == size)
+                               str = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, size, encoding, true);
+                       fclose(fp);
+               }
+               free(path);
+       }
+
+       return str;
+}
+
+STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1) {
+       CFStringRef cfstr0 = (CFStringRef)str0;
+       CFStringRef cfstr1 = (CFStringRef)str1;
+       CFIndex len0 = (cfstr0 ? CFStringGetLength(cfstr0) : 0);
+       CFIndex len1 = (cfstr1 ? CFStringGetLength(cfstr1) : 0);
+       size_t length = (len0 + (ch != 0xffff) + len1);
+
+       UniChar *buf = malloc(sizeof(UniChar) * length);
+       size_t i = 0U;
+
+       if (cfstr0) {
+               CFStringGetCharacters(cfstr0, CFRangeMake(0, len0), buf);
+               i += len0;
+       }
+       if (ch != 0xffff)
+               buf[i++] = ch;
+       if (cfstr1)
+               CFStringGetCharacters(cfstr1, CFRangeMake(0, len1), &buf[i]);
+
+       return CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buf, length, /*contentsDeallocator*/ kCFAllocatorMalloc);
+}
+
+char *copyCString(STRING_TYPE str, CFStringEncoding encoding) {
+       CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), encoding) + 1;
+       char *buffer = calloc(size, 1);
+       CFStringGetCString(str, buffer, size, encoding);
+       return buffer;
+}
+
+STRING_TYPE copyCurrentProcessName(void) {
+       ProcessSerialNumber PSN = { 0, kCurrentProcess };
+       CFStringRef name = NULL;
+       OSStatus err = CopyProcessName(&PSN, &name);
+       if (err != noErr) {
+               NSLog(CFSTR("in copyCurrentProcessName in CFGrowlAdditions: Could not get process name because CopyProcessName returned %li"), (long)err);
+               name = NULL;
+       }
+       return name;
+}
+
+URL_TYPE copyCurrentProcessURL(void) {
+       ProcessSerialNumber psn = { 0, kCurrentProcess };
+       FSRef fsref;
+       CFURLRef URL = NULL;
+       OSStatus err = GetProcessBundleLocation(&psn, &fsref);
+       if (err != noErr) {
+               NSLog(CFSTR("in copyCurrentProcessURL in CFGrowlAdditions: Could not get application location, because GetProcessBundleLocation returned %li\n"), (long)err);
+       } else {
+               URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref);
+       }
+       return URL;
+}
+STRING_TYPE copyCurrentProcessPath(void) {
+       CFURLRef URL = copyCurrentProcessURL();
+       CFStringRef path = CFURLCopyFileSystemPath(URL, kCFURLPOSIXPathStyle);
+       CFRelease(URL);
+       return path;
+}
+
+URL_TYPE copyTemporaryFolderURL(void) {
+       FSRef ref;
+       CFURLRef url = NULL;
+
+       OSStatus err = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, kCreateFolder, &ref);
+       if (err != noErr)
+               NSLog(CFSTR("in copyTemporaryFolderPath in CFGrowlAdditions: Could not locate temporary folder because FSFindFolder returned %li"), (long)err);
+       else
+               url = CFURLCreateFromFSRef(kCFAllocatorDefault, &ref);
+
+       return url;
+}
+STRING_TYPE copyTemporaryFolderPath(void) {
+       CFStringRef path = NULL;
+
+       CFURLRef url = copyTemporaryFolderURL();
+       if (url) {
+               path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+               CFRelease(url);
+       }
+
+       return path;
+}
+
+DATA_TYPE readFile(const char *filename)
+{
+       CFDataRef data;
+       // read the file into a CFDataRef
+       FILE *fp = fopen(filename, "r");
+       if (fp) {
+               fseek(fp, 0, SEEK_END);
+               long dataLength = ftell(fp);
+               fseek(fp, 0, SEEK_SET);
+               unsigned char *fileData = malloc(dataLength);
+               fread(fileData, 1, dataLength, fp);
+               fclose(fp);
+               data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, fileData, dataLength, kCFAllocatorMalloc);
+       } else
+               data = NULL;
+
+       return data;
+}
+
+URL_TYPE copyURLForApplication(STRING_TYPE appName)
+{
+       CFURLRef appURL = NULL;
+       OSStatus err = LSFindApplicationForInfo(/*inCreator*/  kLSUnknownCreator,
+                                                                                       /*inBundleID*/ NULL,
+                                                                                       /*inName*/     appName,
+                                                                                       /*outAppRef*/  NULL,
+                                                                                       /*outAppURL*/  &appURL);
+       return (err == noErr) ? appURL : NULL;
+}
+
+STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData) {
+       struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
+       // IPv6 Addresses are "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
+       //      at max, which is 40 bytes (0-terminated)
+       // IPv4 Addresses are "255.255.255.255" at max which is smaller
+       char stringBuffer[40];
+       CFStringRef addressAsString = NULL;
+       if (socketAddress->sa_family == AF_INET) {
+               struct sockaddr_in *ipv4 = (struct sockaddr_in *)socketAddress;
+               if (inet_ntop(AF_INET, &(ipv4->sin_addr), stringBuffer, 40))
+                       addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s:%d"), stringBuffer, ipv4->sin_port);
+               else
+                       addressAsString = CFSTR("IPv4 un-ntopable");
+       } else if (socketAddress->sa_family == AF_INET6) {
+               struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)socketAddress;
+               if (inet_ntop(AF_INET6, &(ipv6->sin6_addr), stringBuffer, 40))
+                       // Suggested IPv6 format (see http://www.faqs.org/rfcs/rfc2732.html)
+                       addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("[%s]:%d"), stringBuffer, ipv6->sin6_port);
+               else
+                       addressAsString = CFSTR("IPv6 un-ntopable");
+       } else
+               addressAsString = CFSTR("neither IPv6 nor IPv4");
+
+       return addressAsString;
+}
+
+STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData) {
+       char hostname[NI_MAXHOST];
+       struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
+       if (getnameinfo(socketAddress, (socklen_t)CFDataGetLength(aAddressData),
+                                       hostname, (socklen_t)sizeof(hostname),
+                                       /*serv*/ NULL, /*servlen*/ 0,
+                                       NI_NAMEREQD))
+               return NULL;
+       else
+               return CFStringCreateWithCString(kCFAllocatorDefault, hostname, kCFStringEncodingASCII);
+}
+
+DATA_TYPE copyIconDataForPath(STRING_TYPE path) {
+       CFDataRef data = NULL;
+
+       //false is probably safest, and is harmless when the object really is a directory.
+       CFURLRef URL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, /*isDirectory*/ false);
+       if (URL) {
+               data = copyIconDataForURL(URL);
+               CFRelease(URL);
+       }
+
+       return data;
+}
+
+DATA_TYPE copyIconDataForURL(URL_TYPE URL)
+{
+       CFDataRef data = NULL;
+
+       if (URL) {
+               FSRef ref;
+               if (CFURLGetFSRef(URL, &ref)) {
+                       IconRef icon = NULL;
+                       SInt16 label_noOneCares;
+                       OSStatus err = GetIconRefFromFileInfo(&ref,
+                                                                                                 /*inFileNameLength*/ 0U, /*inFileName*/ NULL,
+                                                                                                 kFSCatInfoNone, /*inCatalogInfo*/ NULL,
+                                                                                                 kIconServicesNoBadgeFlag | kIconServicesUpdateIfNeededFlag,
+                                                                                                 &icon,
+                                                                                                 &label_noOneCares);
+                       if (err != noErr) {
+                               NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: GetIconRefFromFileInfo returned %li\n"), URL, (long)err);
+                       } else {
+                               IconFamilyHandle fam = NULL;
+                               err = IconRefToIconFamily(icon, kSelectorAllAvailableData, &fam);
+                               if (err != noErr) {
+                                       NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: IconRefToIconFamily returned %li\n"), URL, (long)err);
+                               } else {
+                                       HLock((Handle)fam);
+                                       data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)*(Handle)fam, GetHandleSize((Handle)fam));
+                                       HUnlock((Handle)fam);
+                                       DisposeHandle((Handle)fam);
+                               }
+                               ReleaseIconRef(icon);
+                       }
+               }
+       }
+
+       return data;
+}
+
+URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name)
+{
+       CFURLRef newDirectory = NULL;
+
+       CFAllocatorRef allocator = parent ? CFGetAllocator(parent) : name ? CFGetAllocator(name) : kCFAllocatorDefault;
+
+       if (parent) parent = CFRetain(parent);
+       else {
+               char *cwdBytes = alloca(PATH_MAX);
+               getcwd(cwdBytes, PATH_MAX);
+               parent = CFURLCreateFromFileSystemRepresentation(allocator, (const unsigned char *)cwdBytes, strlen(cwdBytes), /*isDirectory*/ true);
+               if (!name) {
+                       newDirectory = parent;
+                       goto end;
+               }
+       }
+       if (!parent)
+               NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: parent directory URL is NULL (please tell the Growl developers)\n"), parent);
+       else {
+               if (name)
+                       name = CFRetain(name);
+               else {
+                       name = CFURLCopyLastPathComponent(parent);
+                       CFURLRef newParent = CFURLCreateCopyDeletingLastPathComponent(allocator, parent);
+                       CFRelease(parent);
+                       parent = newParent;
+               }
+
+               if (!name)
+                       NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: name of directory to create is NULL (please tell the Growl developers)\n"), parent);
+               else {
+                       FSRef parentRef;
+                       if (!CFURLGetFSRef(parent, &parentRef))
+                               NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create FSRef for parent directory at %@ (please tell the Growl developers)\n"), parent);
+                       else {
+                               FSRef newDirectoryRef;
+
+                               struct HFSUniStr255 nameUnicode;
+                               CFRange range = { 0, MIN(CFStringGetLength(name), USHRT_MAX) };
+                               CFStringGetCharacters(name, range, nameUnicode.unicode);
+                               nameUnicode.length = range.length;
+
+                               struct FSRefParam refPB = {
+                                       .ref              = &parentRef,
+                                       .nameLength       = nameUnicode.length,
+                                       .name             = nameUnicode.unicode,
+                                       .whichInfo        = kFSCatInfoNone,
+                                       .catInfo          = NULL,
+                                       .textEncodingHint = kTextEncodingUnknown,
+                                       .newRef           = &newDirectoryRef,
+                               };
+
+                               OSStatus err = PBCreateDirectoryUnicodeSync(&refPB);
+                               if (err == dupFNErr) {
+                                       //dupFNErr == file (or folder) exists already. this is fine.
+                                       err = PBMakeFSRefUnicodeSync(&refPB);
+                               }
+                               if (err == noErr) {
+                                       NSLog(CFSTR("PBCreateDirectoryUnicodeSync or PBMakeFSRefUnicodeSync returned %li; calling CFURLCreateFromFSRef"), (long)err); //XXX
+                                       newDirectory = CFURLCreateFromFSRef(allocator, &newDirectoryRef);
+                                       NSLog(CFSTR("CFURLCreateFromFSRef returned %@"), newDirectory); //XXX
+                               } else
+                                       NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create directory '%@' in parent directory at %@: FSCreateDirectoryUnicode returned %li (please tell the Growl developers)"), name, parent, (long)err);
+                       }
+
+               } //if (name)
+               if(parent)
+                       CFRelease(parent);
+               if(name)
+                       CFRelease(name);
+       } //if (parent)
+
+end:
+       return newDirectory;
+}
+
+#ifndef COPYFORK_BUFSIZE
+#      define COPYFORK_BUFSIZE 5242880U /*5 MiB*/
+#endif
+
+static OSStatus copyFork(const struct HFSUniStr255 *forkName, const FSRef *srcFile, const FSRef *destDir, const struct HFSUniStr255 *destName, FSRef *outDestFile) {
+       OSStatus err, closeErr;
+       struct FSForkIOParam srcPB = {
+               .ref = srcFile,
+               .forkNameLength = forkName->length,
+               .forkName = forkName->unicode,
+               .permissions = fsRdPerm,
+       };
+       unsigned char debuggingPathBuf[PATH_MAX] = "";
+       OSStatus debuggingPathErr;
+
+       err = PBOpenForkSync(&srcPB);
+       if (err != noErr) {
+               debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
+               if (debuggingPathErr != noErr)
+                       snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+               NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+       } else {
+               FSRef destFile;
+
+               /*the first thing to do is get the name of the destination file, if one
+                *      wasn't provided.
+                *and while we're at it, we get the catalogue info as well.
+                */
+               struct FSCatalogInfo catInfo;
+               struct FSRefParam refPB = {
+                       .ref       = srcFile,
+                       .whichInfo = kFSCatInfoGettableInfo & kFSCatInfoSettableInfo,
+                       .catInfo   = &catInfo,
+                       .spec      = NULL,
+                       .parentRef = NULL,
+                       .outName   = destName ? NULL : (struct HFSUniStr255 *)(destName = alloca(sizeof(struct HFSUniStr255))),
+               };
+
+               err = PBGetCatalogInfoSync(&refPB);
+               if (err != noErr) {
+                       debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
+                       if (debuggingPathErr != noErr)
+                               snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                       NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBGetCatalogInfoSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+               } else {
+                       refPB.ref              = destDir;
+                       refPB.nameLength       = destName->length;
+                       refPB.name             = destName->unicode;
+                       refPB.textEncodingHint = kTextEncodingUnknown;
+                       refPB.newRef           = &destFile;
+
+                       const char *functionName = "PBMakeFSRefUnicodeSync"; //for error-reporting message
+
+                       err = PBMakeFSRefUnicodeSync(&refPB);
+                       if ((err != noErr) && (err != fnfErr)) {
+                       handleMakeFSRefError:
+                               debuggingPathErr = FSRefMakePath(destDir, debuggingPathBuf, PATH_MAX);
+                               if (debuggingPathErr != noErr)
+                                       snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for destination directory: FSRefMakePath returned %li)", (long)debuggingPathErr);
+
+                               //get filename too
+                               CFStringRef debuggingFilename = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
+                                                                                                                                                                  destName->unicode,
+                                                                                                                                                                  destName->length,
+                                                                                                                                                                  /*contentsDeallocator*/ kCFAllocatorNull);
+                               if (!debuggingFilename)
+                                       debuggingFilename = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, "(could not get filename for destination file: CFStringCreateWithCharactersNoCopy returned NULL)", kCFStringEncodingASCII, /*contentsDeallocator*/ kCFAllocatorNull);
+
+                               NSLog(CFSTR("in copyFork in CFGrowlAdditions: %s (destination: %s/%@) returned %li"), functionName, debuggingPathBuf, debuggingFilename, (long)err);
+
+                               if (debuggingFilename) CFRelease(debuggingFilename);
+                       } else {
+                               //that file doesn't exist in that folder; create it.
+                               err = PBCreateFileUnicodeSync(&refPB);
+                               if (err == noErr) {
+                                       /*make sure the Finder knows about the new file.
+                                        *FNNotify returns a status code too, but this isn't an
+                                        *      essential step, so we just ignore it.
+                                        */
+                                       FNNotify(destDir, kFNDirectoryModifiedMessage, kNilOptions);
+                               } else if (err == dupFNErr) {
+                                       /*dupFNErr: the file already exists.
+                                        *we can safely ignore this error.
+                                        */
+                                       err = noErr;
+                               } else {
+                                       functionName = "PBCreateFileUnicodeSync";
+                                       goto handleMakeFSRefError;
+                               }
+                       }
+               }
+               if (err == noErr) {
+                       if (outDestFile)
+                               memcpy(outDestFile, &destFile, sizeof(destFile));
+
+                       struct FSForkIOParam destPB = {
+                               .ref            = &destFile,
+                               .forkNameLength = forkName->length,
+                               .forkName       = forkName->unicode,
+                               .permissions    = fsWrPerm,
+                       };
+                       err = PBOpenForkSync(&destPB);
+                       NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (dest) returned %li"), (long)err);
+                       if (err != noErr) {
+                               debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+                               if (debuggingPathErr != noErr)
+                                       snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                               NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+                       } else {
+                               void *buf = malloc(COPYFORK_BUFSIZE);
+                               if (buf) {
+                                       srcPB.buffer = destPB.buffer = buf;
+                                       srcPB.requestCount = COPYFORK_BUFSIZE;
+                                       while (err == noErr) {
+                                               err = PBReadForkSync(&srcPB);
+                                               if (err == eofErr) {
+                                                       err = noErr;
+                                                       if (srcPB.actualCount == 0)
+                                                               break;
+                                               }
+                                               if (err != noErr) {
+                                                       debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+                                                       if (debuggingPathErr != noErr)
+                                                               snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                                                       NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBReadForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+                                               } else {
+                                                       destPB.requestCount = srcPB.actualCount;
+                                                       err = PBWriteForkSync(&destPB);
+                                                       if (err != noErr) {
+                                                               debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+                                                               if (debuggingPathErr != noErr)
+                                                                       snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                                                               NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBWriteForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+                                                       }
+                                               }
+                                       }
+
+                                       free(buf);
+                               }
+
+                               closeErr = PBCloseForkSync(&destPB);
+                               if (closeErr != noErr) {
+                                       debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+                                       if (debuggingPathErr != noErr)
+                                               snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                                       NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+                               }
+                               if (err == noErr) err = closeErr;
+                       }
+               }
+
+               closeErr = PBCloseForkSync(&srcPB);
+               if (closeErr != noErr) {
+                       debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+                       if (debuggingPathErr != noErr)
+                               snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+                       NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+               }
+               if (err == noErr) err = closeErr;
+       }
+
+       return err;
+}
+
+static OSStatus GrowlCopyObjectSync(const FSRef *fileRef, const FSRef *destRef, FSRef *destFileRef) {
+       OSStatus err;
+       struct HFSUniStr255 forkName;
+       struct FSForkIOParam forkPB = {
+               .ref = fileRef,
+               .forkIterator = {
+                       .initialize = 0
+               },
+               .outForkName = &forkName,
+       };
+
+       do {
+               err = PBIterateForksSync(&forkPB);
+               NSLog(CFSTR("PBIterateForksSync returned %li"), (long)err);
+               if (err != noErr) {
+                       if (err != errFSNoMoreItems)
+                               NSLog(CFSTR("in GrowlCopyObjectSync in CFGrowlAdditions: PBIterateForksSync returned %li"), (long)err);
+               } else {
+                       err = copyFork(&forkName, fileRef, destRef, /*destName*/ NULL, /*outDestFile*/ destFileRef);
+                       //copyFork prints its own error messages
+               }
+       } while (err == noErr);
+       if (err == errFSNoMoreItems) err = noErr;
+
+       return err;
+}
+
+URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest)
+{
+       CFURLRef destFileURL = NULL;
+
+       FSRef fileRef, destRef, destFileRef;
+       Boolean gotFileRef = CFURLGetFSRef(file, &fileRef);
+       Boolean gotDestRef = CFURLGetFSRef(dest, &destRef);
+       if (!gotFileRef)
+               NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with source URL %@"), file);
+       else if (!gotDestRef)
+               NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with destination URL %@"), dest);
+       else {
+               OSStatus err;
+
+               /*
+                * 10.2 has a problem with weak symbols in frameworks so we use
+                * MAC_OS_X_VERSION_MIN_REQUIRED >= 10.3.
+                */
+#if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
+               if (FSCopyObjectSync) {
+                       err = FSCopyObjectSync(&fileRef, &destRef, /*destName*/ NULL, &destFileRef, kFSFileOperationOverwrite);
+               } else {
+#endif
+                       err = GrowlCopyObjectSync(&fileRef, &destRef, &destFileRef);
+#if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
+               }
+#endif
+
+               if (err == noErr)
+                       destFileURL = CFURLCreateFromFSRef(kCFAllocatorDefault, &destFileRef);
+               else
+                       NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CopyObjectSync returned %li for source URL %@"), (long)err, file);
+       }
+
+       return destFileURL;
+}
+
+PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString)
+{
+       CFPropertyListRef plist = NULL;
+
+       if (!file)
+               NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: cannot read from a NULL URL"));
+       else {
+               CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, file);
+               if (!stream)
+                       NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not create stream for reading from URL %@"), file);
+               else {
+                       if (!CFReadStreamOpen(stream))
+                               NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not open stream for reading from URL %@"), file);
+                       else {
+                               CFPropertyListFormat format;
+                               CFStringRef errorString = NULL;
+
+                               plist = CFPropertyListCreateFromStream(kCFAllocatorDefault,
+                                                                                                          stream,
+                                                                                                          /*streamLength*/ 0,
+                                                                                                          mutability,
+                                                                                                          &format,
+                                                                                                          &errorString);
+                               if (!plist)
+                                       NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not read property list from URL %@ (error string: %@)"), file, errorString);
+
+                               if (outFormat) *outFormat = format;
+                               if (errorString) {
+                                       if (outErrorString)
+                                               *outErrorString = errorString;
+                                       else
+                                               CFRelease(errorString);
+                               }
+
+                               CFReadStreamClose(stream);
+                       }
+
+                       CFRelease(stream);
+               }
+       }
+
+       return plist;
+}
diff --git a/src/platform/DARWIN/growlagent/CFGrowlAdditions.h b/src/platform/DARWIN/growlagent/CFGrowlAdditions.h
new file mode 100644 (file)
index 0000000..a65d78d
--- /dev/null
@@ -0,0 +1,86 @@
+//
+//  CFGrowlAdditions.h
+//  Growl
+//
+//  Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
+//  Copyright 2005-2006 The Growl Project.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFGROWLADDITIONS_H
+#define HAVE_CFGROWLADDITIONS_H
+
+#include "CFGrowlDefines.h"
+
+//see GrowlApplicationBridge-Carbon.c for rationale of using NSLog.
+extern void NSLog(STRING_TYPE format, ...);
+
+char *createFileSystemRepresentationOfString(STRING_TYPE str);
+STRING_TYPE createStringWithDate(DATE_TYPE date);
+
+STRING_TYPE createStringWithContentsOfFile(STRING_TYPE filename, CFStringEncoding encoding);
+
+//you can leave out any of these three components. to leave out the character, pass 0xffff.
+STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1);
+
+char *copyCString(STRING_TYPE str, CFStringEncoding encoding);
+
+STRING_TYPE copyCurrentProcessName(void);
+URL_TYPE    copyCurrentProcessURL(void);
+STRING_TYPE copyCurrentProcessPath(void);
+
+URL_TYPE    copyTemporaryFolderURL(void);
+STRING_TYPE copyTemporaryFolderPath(void);
+
+STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData);
+STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData);
+
+DATA_TYPE readFile(const char *filename);
+URL_TYPE  copyURLForApplication(STRING_TYPE appName);
+
+/*     @function       copyIconDataForPath
+ *     @param  path    The POSIX path to the file or folder whose icon you want.
+ *     @result The icon data, in IconFamily format (same as used in the 'icns' resource and in .icns files). You are responsible for releasing this object.
+ */
+DATA_TYPE copyIconDataForPath(STRING_TYPE path);
+/*     @function       copyIconDataForURL
+ *     @param  URL     The URL to the file or folder whose icon you want.
+ *     @result The icon data, in IconFamily format (same as used in the 'icns' resource and in .icns files). You are responsible for releasing this object.
+ */
+DATA_TYPE copyIconDataForURL(URL_TYPE URL);
+
+/*     @function       createURLByMakingDirectoryAtURLWithName
+ *     @abstract       Create a directory.
+ *     @discussion     This function has a useful side effect: if you pass
+ *      <code>NULL</code> for both parameters, this function will act basically as
+ *      CFURL version of <code>getcwd</code>(3).
+ *
+ *      Also, for CF clients: the allocator used to create the returned URL will
+ *      be the allocator for the parent URL, the allocator for the name string, or
+ *      the default allocator, in that order of preference.
+ *     @param  parent  The directory in which to create the new directory. If this is <code>NULL</code>, the current working directory (as returned by <code>getcwd</code>(3)) will be used.
+ *     @param  name    The name of the directory you want to create. If this is <code>NULL</code>, the directory specified by the URL will be created.
+ *     @result The URL for the directory if it was successfully created (in which case, you are responsible for releasing this object); else, <code>NULL</code>.
+ */
+URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name);
+
+/*     @function       createURLByCopyingFileFromURLToDirectoryURL
+ *     @param  file    The file to copy.
+ *     @param  dest    The folder to copy it to.
+ *     @result The copy. You are responsible for releasing this object.
+ */
+URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest);
+
+/*     @function       createPropertyListFromURL
+ *     @abstract       Reads a property list from the contents of an URL.
+ *     @discussion     Creates a property list from the data at an URL (for example, a
+ *      file URL), and returns it.
+ *     @param  file    The file to read.
+ *     @param  mutability      A mutability-option constant indicating whether the property list (and possibly its contents) should be mutable.
+ *     @param  outFormat       If the property list is read successfully, this will point to the format of the property list. You may pass NULL if you are not interested in this information. If the property list is not read successfully, the value at this pointer will be left unchanged.
+ *     @param  outErrorString  If an error occurs, this will point to a string (which you are responsible for releasing) describing the error. You may pass NULL if you are not interested in this information. If no error occurs, the value at this pointer will be left unchanged.
+ *     @result The property list. You are responsible for releasing this object.
+ */
+PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString);
+
+#endif
diff --git a/src/platform/DARWIN/growlagent/CFGrowlDefines.h b/src/platform/DARWIN/growlagent/CFGrowlDefines.h
new file mode 100644 (file)
index 0000000..da7c17c
--- /dev/null
@@ -0,0 +1,38 @@
+//
+//  CFURLDefines.h
+//  Growl
+//
+//  Created by Ingmar Stein on Fri Sep 16 2005.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFGROWLDEFINES_H
+#define HAVE_CFGROWLDEFINES_H
+
+#ifdef __OBJC__
+#      define DATA_TYPE                                NSData *
+#      define DATE_TYPE                                NSDate *
+#      define DICTIONARY_TYPE                  NSDictionary *
+#      define MUTABLE_DICTIONARY_TYPE  NSMutableDictionary *
+#      define STRING_TYPE                              NSString *
+#      define ARRAY_TYPE                               NSArray *
+#      define URL_TYPE                                 NSURL *
+#      define PLIST_TYPE                               NSObject *
+#      define OBJECT_TYPE                              id
+#      define BOOL_TYPE                                BOOL
+#else
+#      include <CoreFoundation/CoreFoundation.h>
+#      define DATA_TYPE                                CFDataRef
+#      define DATE_TYPE                                CFDateRef
+#      define DICTIONARY_TYPE                  CFDictionaryRef
+#      define MUTABLE_DICTIONARY_TYPE  CFMutableDictionaryRef
+#      define STRING_TYPE                              CFStringRef
+#      define ARRAY_TYPE                               CFArrayRef
+#      define URL_TYPE                                 CFURLRef
+#      define PLIST_TYPE                               CFPropertyListRef
+#      define OBJECT_TYPE                              CFTypeRef
+#      define BOOL_TYPE                                Boolean
+#endif
+
+#endif
diff --git a/src/platform/DARWIN/growlagent/GrowlDefines.h b/src/platform/DARWIN/growlagent/GrowlDefines.h
new file mode 100644 (file)
index 0000000..2b971cf
--- /dev/null
@@ -0,0 +1,348 @@
+//
+//  GrowlDefines.h
+//
+
+#ifndef _GROWLDEFINES_H
+#define _GROWLDEFINES_H
+
+#ifdef __OBJC__
+#define XSTR(x) (@x)
+#define STRING_TYPE NSString *
+#else
+#define XSTR CFSTR
+#define STRING_TYPE CFStringRef
+#endif
+
+/*!    @header GrowlDefines.h
+ *     @abstract   Defines all the notification keys.
+ *     @discussion Defines all the keys used for registration with Growl and for
+ *      Growl notifications.
+ *
+ *      Most applications should use the functions or methods of Growl.framework
+ *      instead of posting notifications such as those described here.
+ *     @updated 2004-01-25
+ */
+
+// UserInfo Keys for Registration
+#pragma mark UserInfo Keys for Registration
+
+/*!    @group Registration userInfo keys */
+/*     @abstract       Keys for the userInfo dictionary of a GROWL_APP_REGISTRATION distributed notification.
+ *     @discussion The values of these keys describe the application and the
+ *      notifications it may post.
+ *
+ *      Your application must register with Growl before it can post Growl
+ *      notifications (and have them not be ignored). However, as of Growl 0.6,
+ *      posting GROWL_APP_REGISTRATION notifications directly is no longer the
+ *      preferred way to register your application. Your application should instead
+ *      use Growl.framework's delegate system.
+ *      See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for
+ *      more information.
+ */
+
+/*!    @defined GROWL_APP_NAME
+ *     @abstract The name of your application.
+ *     @discussion The name of your application. This should remain stable between
+ *      different versions and incarnations of your application.
+ *      For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and
+ *      "SurfWriter Lite" are not.
+ */
+#define GROWL_APP_NAME                                 XSTR("ApplicationName")
+/*!    @defined GROWL_APP_ID
+ *     @abstract The bundle identifier of your application.
+ *     @discussion The bundle identifier of your application. This key should
+ *   be unique for your application while there may be several applications
+ *   with the same GROWL_APP_NAME.
+ *   This key is optional.
+ */
+#define GROWL_APP_ID                                   XSTR("ApplicationId")
+/*!    @defined GROWL_APP_ICON
+ *     @abstract The image data for your application's icon.
+ *     @discussion Image data representing your application's icon. This may be
+ *      superimposed on a notification icon as a badge, used as the notification
+ *      icon when a notification-specific icon is not supplied, or ignored
+ *      altogether, depending on the display. Must be in a format supported by
+ *      NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_APP_ICON                                 XSTR("ApplicationIcon")
+/*!    @defined GROWL_NOTIFICATIONS_DEFAULT
+ *     @abstract The array of notifications to turn on by default.
+ *     @discussion These are the names of the notifications that should be enabled
+ *      by default when your application registers for the first time. If your
+ *      application reregisters, Growl will look here for any new notification
+ *      names found in GROWL_NOTIFICATIONS_ALL, but ignore any others.
+ */
+#define GROWL_NOTIFICATIONS_DEFAULT            XSTR("DefaultNotifications")
+/*!    @defined GROWL_NOTIFICATIONS_ALL
+ *     @abstract The array of all notifications your application can send.
+ *     @discussion These are the names of all of the notifications that your
+ *      application may post. See GROWL_NOTIFICATION_NAME for a discussion of good
+ *      notification names.
+ */
+#define GROWL_NOTIFICATIONS_ALL                        XSTR("AllNotifications")
+/*! @defined GROWL_NOTIFICATIONS_HUMAN_READABLE_DESCRIPTIONS
+ *  @abstract A dictionary of human-readable names for your notifications.
+ *  @discussion By default, the Growl UI will display notifications by the names given in GROWL_NOTIFICATIONS_ALL
+ *  which correspond to the GROWL_NOTIFICATION_NAME. This dictionary specifies the human-readable name to display.
+ *  The keys of the dictionary are GROWL_NOTIFICATION_NAME strings; the objects are the human-readable versions.
+ *  For any GROWL_NOTIFICATION_NAME not specific in this dictionary, the GROWL_NOTIFICATION_NAME will be displayed.
+ *
+ *  This key is optional.
+ */
+#define GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES               XSTR("HumanReadableNames")
+/*! @defined GROWL_NOTIFICATIONS_DESCRIPTIONS
+*  @abstract A dictionary of descriptions of _when_ each notification occurs
+*  @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are
+*  descriptions of _when_ each notification occurs, such as "You received a new mail message" or
+*  "A file finished downloading".
+*
+*  This key is optional.
+*/
+#define GROWL_NOTIFICATIONS_DESCRIPTIONS               XSTR("NotificationDescriptions")
+
+/*!    @defined        GROWL_TICKET_VERSION
+ *     @abstract       The version of your registration ticket.
+ *     @discussion     Include this key in a ticket plist file that you put in your
+ *      application bundle for auto-discovery. The current ticket version is 1.
+ */
+#define GROWL_TICKET_VERSION                   XSTR("TicketVersion")
+// UserInfo Keys for Notifications
+#pragma mark UserInfo Keys for Notifications
+
+/*!    @group Notification userInfo keys */
+/*     @abstract       Keys for the userInfo dictionary of a GROWL_NOTIFICATION distributed notification.
+ *     @discussion The values of these keys describe the content of a Growl
+ *      notification.
+ *
+ *      Not all of these keys are supported by all displays. Only the name, title,
+ *      and description of a notification are universal. Most of the built-in
+ *      displays do support all of these keys, and most other visual displays
+ *      probably will also. But, as of 0.6, the Log, MailMe, and Speech displays
+ *      support only textual data.
+ */
+
+/*!    @defined GROWL_NOTIFICATION_NAME
+ *     @abstract The name of the notification.
+ *     @discussion The name of the notification. Note that if you do not define
+ *  GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES when registering your ticket originally this name
+ *  will the one displayed within the Growl preference pane and should be human-readable.
+ */
+#define GROWL_NOTIFICATION_NAME                        XSTR("NotificationName")
+/*!    @defined GROWL_NOTIFICATION_TITLE
+ *     @abstract The title to display in the notification.
+ *     @discussion The title of the notification. Should be very brief.
+ *      The title usually says what happened, e.g. "Download complete".
+ */
+#define GROWL_NOTIFICATION_TITLE               XSTR("NotificationTitle")
+/*!    @defined GROWL_NOTIFICATION_DESCRIPTION
+ *     @abstract The description to display in the notification.
+ *     @discussion The description should be longer and more verbose than the title.
+ *      The description usually tells the subject of the action,
+ *      e.g. "Growl-0.6.dmg downloaded in 5.02 minutes".
+ */
+#define GROWL_NOTIFICATION_DESCRIPTION         XSTR("NotificationDescription")
+/*!    @defined GROWL_NOTIFICATION_ICON
+ *     @discussion Image data for the notification icon. Must be in a format
+ *      supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_ICON                        XSTR("NotificationIcon")
+/*!    @defined GROWL_NOTIFICATION_APP_ICON
+ *     @discussion Image data for the application icon, in case GROWL_APP_ICON does
+ *      not apply for some reason. Must be in a format supported by NSImage, such
+ *      as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_APP_ICON            XSTR("NotificationAppIcon")
+/*!    @defined GROWL_NOTIFICATION_PRIORITY
+ *     @discussion The priority of the notification as an integer number from
+ *      -2 to +2 (+2 being highest).
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_PRIORITY            XSTR("NotificationPriority")
+/*!    @defined GROWL_NOTIFICATION_STICKY
+ *     @discussion A Boolean number controlling whether the notification is sticky.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_STICKY              XSTR("NotificationSticky")
+/*!    @defined GROWL_NOTIFICATION_CLICK_CONTEXT
+ *     @abstract Identifies which notification was clicked.
+ *     @discussion An identifier for the notification for clicking purposes.
+ *
+ *      This will be passed back to the application when the notification is
+ *      clicked. It must be plist-encodable (a data, dictionary, array, number, or
+ *      string object), and it should be unique for each notification you post.
+ *      A good click context would be a UUID string returned by NSProcessInfo or
+ *      CFUUID.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_CLICK_CONTEXT                       XSTR("NotificationClickContext")
+
+/*!    @defined GROWL_DISPLAY_PLUGIN
+ *     @discussion The name of a display plugin which should be used for this notification.
+ *    Optional. If this key is not set or the specified display plugin does not
+ *    exist, the display plugin stored in the application ticket is used. This key
+ *    allows applications to use different default display plugins for their
+ *    notifications. The user can still override those settings in the preference
+ *    pane.
+ */
+#define GROWL_DISPLAY_PLUGIN                           XSTR("NotificationDisplayPlugin")
+
+/*!    @defined GROWL_NOTIFICATION_IDENTIFIER
+ *     @abstract An identifier for the notification for coalescing purposes.
+ *   Notifications with the same identifier fall into the same class; only
+ *   the last notification of a class is displayed on the screen. If a
+ *   notification of the same class is currently being displayed, it is
+ *   replaced by this notification.
+ *
+ *      Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_IDENTIFIER  XSTR("GrowlNotificationIdentifier")
+
+/*!    @defined GROWL_APP_PID
+ *     @abstract The process identifier of the process which sends this
+ *   notification. If this field is set, the application will only receive
+ *   clicked and timed out notifications which originate from this process.
+ *
+ *      Optional.
+ */
+#define GROWL_APP_PID                                  XSTR("ApplicationPID")
+
+/*!    @defined GROWL_NOTIFICATION_PROGRESS
+*      @abstract If this key is set, it should contain a double value wrapped
+*     in a NSNumber which describes some sort of progress (from 0.0 to 100.0).
+*     If this is key is not set, no progress bar is shown.
+*
+*       Optional. Not supported by all display plugins.
+*/
+#define GROWL_NOTIFICATION_PROGRESS            XSTR("NotificationProgress")
+
+// Notifications
+#pragma mark Notifications
+
+/*!    @group Notification names */
+/*     @abstract       Names of distributed notifications used by Growl.
+ *     @discussion     These are notifications used by applications (directly or
+ *      indirectly) to interact with Growl, and by Growl for interaction between
+ *      its components.
+ *
+ *      Most of these should no longer be used in Growl 0.6 and later, in favor of
+ *      Growl.framework's GrowlApplicationBridge APIs.
+ */
+
+/*!    @defined GROWL_APP_REGISTRATION
+ *     @abstract The distributed notification for registering your application.
+ *     @discussion This is the name of the distributed notification that can be
+ *      used to register applications with Growl.
+ *
+ *      The userInfo dictionary for this notification can contain these keys:
+ *      <ul>
+ *             <li>GROWL_APP_NAME</li>
+ *             <li>GROWL_APP_ICON</li>
+ *             <li>GROWL_NOTIFICATIONS_ALL</li>
+ *             <li>GROWL_NOTIFICATIONS_DEFAULT</li>
+ *      </ul>
+ *
+ *      No longer recommended as of Growl 0.6. An alternate method of registering
+ *      is to use Growl.framework's delegate system.
+ *      See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for
+ *      more information.
+ */
+#define GROWL_APP_REGISTRATION                 XSTR("GrowlApplicationRegistrationNotification")
+/*!    @defined GROWL_APP_REGISTRATION_CONF
+ *     @abstract The distributed notification for confirming registration.
+ *     @discussion The name of the distributed notification sent to confirm the
+ *      registration. Used by the Growl preference pane. Your application probably
+ *      does not need to use this notification.
+ */
+#define GROWL_APP_REGISTRATION_CONF            XSTR("GrowlApplicationRegistrationConfirmationNotification")
+/*!    @defined GROWL_NOTIFICATION
+ *     @abstract The distributed notification for Growl notifications.
+ *     @discussion This is what it all comes down to. This is the name of the
+ *      distributed notification that your application posts to actually send a
+ *      Growl notification.
+ *
+ *      The userInfo dictionary for this notification can contain these keys:
+ *      <ul>
+ *             <li>GROWL_NOTIFICATION_NAME (required)</li>
+ *             <li>GROWL_NOTIFICATION_TITLE (required)</li>
+ *             <li>GROWL_NOTIFICATION_DESCRIPTION (required)</li>
+ *             <li>GROWL_NOTIFICATION_ICON</li>
+ *             <li>GROWL_NOTIFICATION_APP_ICON</li>
+ *             <li>GROWL_NOTIFICATION_PRIORITY</li>
+ *             <li>GROWL_NOTIFICATION_STICKY</li>
+ *             <li>GROWL_NOTIFICATION_CLICK_CONTEXT</li>
+ *             <li>GROWL_APP_NAME (required)</li>
+ *      </ul>
+ *
+ *      No longer recommended as of Growl 0.6. Three alternate methods of posting
+ *      notifications are +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:],
+ *      Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext, and
+ *      Growl_PostNotification.
+ */
+#define GROWL_NOTIFICATION                             XSTR("GrowlNotification")
+/*!    @defined GROWL_SHUTDOWN
+*      @abstract The distributed notification name that tells Growl to shutdown.
+*      @discussion The Growl preference pane posts this notification when the
+*       "Stop Growl" button is clicked.
+*/
+#define GROWL_SHUTDOWN                                 XSTR("GrowlShutdown")
+/*!    @defined GROWL_PING
+ *     @abstract A distributed notification to check whether Growl is running.
+ *     @discussion This is used by the Growl preference pane. If it receives a
+ *      GROWL_PONG, the preference pane takes this to mean that Growl is running.
+ */
+#define GROWL_PING                                             XSTR("Honey, Mind Taking Out The Trash")
+/*!    @defined GROWL_PONG
+ *     @abstract The distributed notification sent in reply to GROWL_PING.
+ *     @discussion GrowlHelperApp posts this in reply to GROWL_PING.
+ */
+#define GROWL_PONG                                             XSTR("What Do You Want From Me, Woman")
+/*!    @defined GROWL_IS_READY
+ *     @abstract The distributed notification sent when Growl starts up.
+ *     @discussion GrowlHelperApp posts this when it has begin listening on all of
+ *      its sources for new notifications. GrowlApplicationBridge (in
+ *      Growl.framework), upon receiving this notification, reregisters using the
+ *      registration dictionary supplied by its delegate.
+ */
+#define GROWL_IS_READY                                 XSTR("Lend Me Some Sugar; I Am Your Neighbor!")
+/*!    @defined GROWL_NOTIFICATION_CLICKED
+ *     @abstract The distributed notification sent when a supported notification is clicked.
+ *     @discussion When a Growl notification with a click context is clicked on by
+ *      the user, Growl posts this distributed notification.
+ *      The GrowlApplicationBridge responds to this notification by calling a
+ *      callback in its delegate.
+ */
+#define GROWL_NOTIFICATION_CLICKED             XSTR("GrowlClicked!")
+#define GROWL_NOTIFICATION_TIMED_OUT   XSTR("GrowlTimedOut!")
+
+/*!    @group Other symbols */
+/* Symbols which don't fit into any of the other categories. */
+
+/*!    @defined GROWL_KEY_CLICKED_CONTEXT
+ *     @abstract Used internally as the key for the clickedContext passed over DNC.
+ *     @discussion This key is used in GROWL_NOTIFICATION_CLICKED, and contains the
+ *      click context that was supplied in the original notification.
+ */
+#define GROWL_KEY_CLICKED_CONTEXT              XSTR("ClickedContext")
+/*!    @defined GROWL_REG_DICT_EXTENSION
+ *     @abstract The filename extension for registration dictionaries.
+ *     @discussion The GrowlApplicationBridge in Growl.framework registers with
+ *      Growl by creating a file with the extension of .(GROWL_REG_DICT_EXTENSION)
+ *      and opening it in the GrowlHelperApp. This happens whether or not Growl is
+ *      running; if it was stopped, it quits immediately without listening for
+ *      notifications.
+ */
+#define GROWL_REG_DICT_EXTENSION               XSTR("growlRegDict")
+
+
+#define GROWL_POSITION_PREFERENCE_KEY                  @"GrowlSelectedPosition"
+
+#endif //ndef _GROWLDEFINES_H
diff --git a/src/platform/DARWIN/growlagent/GrowlDefinesInternal.h b/src/platform/DARWIN/growlagent/GrowlDefinesInternal.h
new file mode 100644 (file)
index 0000000..c8ab20b
--- /dev/null
@@ -0,0 +1,489 @@
+//
+//  GrowlDefinesInternal.h
+//  Growl
+//
+//  Created by Karl Adam on Mon May 17 2004.
+//  Copyright (c) 2004 the Growl Project. All rights reserved.
+//
+
+#ifndef _GROWL_GROWLDEFINESINTERNAL_H
+#define _GROWL_GROWLDEFINESINTERNAL_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __OBJC__
+#define XSTR(x) (@x)
+#else /* !__OBJC__ */
+#define XSTR CFSTR
+#endif /* __OBJC__ */
+
+/*!    @header GrowlDefinesInternal.h
+ *     @abstract       Defines internal Growl macros and types.
+ *  @ignore ATTRIBUTE_PACKED
+ *     @discussion     These constants are used both by GrowlHelperApp and by plug-ins.
+ *
+ *      Notification keys (used in GrowlHelperApp, in GrowlApplicationBridge, and
+ *      by applications that don't use GrowlApplicationBridge) are defined in
+ *      GrowlDefines.h.
+ */
+
+/*!
+ * @defined NSInteger
+ * @abstract Typedef to int so Growl will compile on pre-10.5 SDKs.
+ */
+#ifndef NSINTEGER_DEFINED
+typedef int NSInteger;
+typedef unsigned int NSUInteger;
+#define NSINTEGER_DEFINED
+#endif
+
+/*!
+ * @defined CGFloat
+ * @abstract Typedef to float so Growl will compile on pre-10.5 SDKs.
+ */
+#ifndef CGFLOAT_DEFINED
+typedef float CGFloat;
+#define CGFLOAT_IS_DOUBLE 0
+#define CGFLOAT_DEFINED
+#endif
+
+/*!
+ * @defined GrowlCGFloatCeiling()
+ * @abstract Macro for the ceil() function that uses a different precision depending on the CPU architecture.
+ */
+/*!
+ * @defined GrowlCGFloatAbsoluteValue()
+ * @abstract Macro for the fabs() function that uses a different precision depending on the CPU architecture.
+ */
+/*!
+ * @defined GrowlCGFloatFloor()
+ * @abstract Macro for the floor() function that uses a different precision depending on the CPU architecture.
+ */
+#if CGFLOAT_IS_DOUBLE
+#define GrowlCGFloatCeiling(x) ceil(x)
+#define GrowlCGFloatAbsoluteValue(x) fabs(x)
+#define GrowlCGFloatFloor(x) floor(x)
+#else
+#define GrowlCGFloatCeiling(x) ceilf(x)
+#define GrowlCGFloatAbsoluteValue(x) fabsf(x)
+#define GrowlCGFloatFloor(x) floorf(x)
+#endif
+
+/*!    @defined        GROWL_TCP_PORT
+ *     @abstract       The TCP listen port for Growl notification servers.
+ */
+#define GROWL_TCP_PORT 23052
+
+/*!    @defined        GROWL_UDP_PORT
+ *     @abstract       The UDP listen port for Growl notification servers.
+ */
+#define GROWL_UDP_PORT 9887
+
+/*!    @defined        GROWL_PROTOCOL_VERSION
+ *     @abstract       The current version of the Growl network-notifications protocol (without encryption).
+ */
+#define GROWL_PROTOCOL_VERSION 1
+
+/*!    @defined        GROWL_PROTOCOL_VERSION_AES128
+*      @abstract       The current version of the Growl network-notifications protocol (with AES-128 encryption).
+*/
+#define GROWL_PROTOCOL_VERSION_AES128  2
+
+/*!    @defined        GROWL_TYPE_REGISTRATION
+ *     @abstract       The packet type of registration packets with MD5 authentication.
+ */
+#define GROWL_TYPE_REGISTRATION                        0
+/*!    @defined        GROWL_TYPE_NOTIFICATION
+ *     @abstract       The packet type of notification packets with MD5 authentication.
+ */
+#define GROWL_TYPE_NOTIFICATION                        1
+/*!    @defined        GROWL_TYPE_REGISTRATION_SHA256
+ *     @abstract       The packet type of registration packets with SHA-256 authentication.
+ */
+#define GROWL_TYPE_REGISTRATION_SHA256 2
+/*!    @defined        GROWL_TYPE_NOTIFICATION_SHA256
+ *     @abstract       The packet type of notification packets with SHA-256 authentication.
+ */
+#define GROWL_TYPE_NOTIFICATION_SHA256 3
+/*!    @defined        GROWL_TYPE_REGISTRATION_NOAUTH
+*      @abstract       The packet type of registration packets without authentication.
+*/
+#define GROWL_TYPE_REGISTRATION_NOAUTH 4
+/*!    @defined        GROWL_TYPE_NOTIFICATION_NOAUTH
+*      @abstract       The packet type of notification packets without authentication.
+*/
+#define GROWL_TYPE_NOTIFICATION_NOAUTH 5
+
+#define ATTRIBUTE_PACKED __attribute((packed))
+
+/*!    @struct GrowlNetworkPacket
+ *     @abstract       This struct is a header common to all incoming Growl network
+ *      packets which identifies the type and version of the packet.
+ */
+struct GrowlNetworkPacket {
+       unsigned char version;
+       unsigned char type;
+} ATTRIBUTE_PACKED;
+
+/*!
+ * @struct GrowlNetworkRegistration
+ * @abstract The format of a registration packet.
+ * @discussion A Growl client that wants to register with a Growl server sends
+ * a packet in this format.
+ * @field common The Growl packet header.
+ * @field appNameLen The name of the application that is registering.
+ * @field numAllNotifications The number of notifications in the list.
+ * @field numDefaultNotifications The number of notifications in the list that are enabled by default.
+ * @field data Variable-sized data.
+ */
+struct GrowlNetworkRegistration {
+       struct GrowlNetworkPacket common;
+       /*      This name is used both internally and in the Growl
+        *       preferences.
+        *
+        *       The application name should remain stable between different versions
+        *       and incarnations of your application.
+        *       For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0"
+        *       and "SurfWriter Lite" are not.
+        *
+        *       In addition to being unsigned, the application name length is in
+        *       network byte order.
+        */
+       unsigned short appNameLen;
+       /*      These names are used both internally and in the Growl
+        *       preferences. For this reason, they should be human-readable.
+        */
+       unsigned char numAllNotifications;
+
+       unsigned char numDefaultNotifications;
+       /*      The variable-sized data of a registration is:
+        *       - The application name, in UTF-8 encoding, for appNameLen bytes.
+        *       - The list of all notification names.
+        *       - The list of default notifications, as 8-bit unsigned indices into the list of all notifications.
+        *       - The MD5/SHA256 checksum of all the data preceding the checksum.
+        *
+        *       Each notification name is encoded as:
+        *       - Length: two bytes, unsigned, network byte order.
+        *       - Name: As many bytes of UTF-8-encoded text as the length says.
+        *       And there are numAllNotifications of these.
+        */
+       unsigned char data[];
+} ATTRIBUTE_PACKED;
+
+/*!
+ * @struct GrowlNetworkNotification
+ * @abstract The format of a notification packet.
+ * @discussion A Growl client that wants to post a notification to a Growl
+ * server sends a packet in this format.
+ * @field common The Growl packet header.
+ * @field flags The priority number and the sticky bit.
+ * @field nameLen The length of the notification name.
+ * @field titleLen The length of the notification title.
+ * @field descriptionLen The length of the notification description.
+ * @field appNameLen The length of the application name.
+ * @field data Variable-sized data.
+ */
+struct GrowlNetworkNotification {
+       struct GrowlNetworkPacket common;
+       /*!
+        * @struct GrowlNetworkNotificationFlags
+        * @abstract Various flags.
+        * @discussion This 16-bit packed structure contains the priority as a
+        *  signed 3-bit integer from -2 to +2, and the sticky flag as a single bit.
+        *  The high 12 bits of the structure are reserved for future use.
+        * @field reserved reserved for future use.
+        * @field priority the priority as a signed 3-bit integer from -2 to +2.
+        * @field sticky the sticky flag.
+        */
+       struct GrowlNetworkNotificationFlags {
+#ifdef __BIG_ENDIAN__
+               unsigned reserved: 12;
+               signed   priority: 3;
+               unsigned sticky:   1;
+#else
+               unsigned sticky:   1;
+               signed   priority: 3;
+               unsigned reserved: 12;
+#endif
+       } ATTRIBUTE_PACKED flags; //size = 16 (12 + 3 + 1)
+
+       /*      In addition to being unsigned, the notification name length
+        *       is in network byte order.
+        */
+       unsigned short nameLen;
+       /*      @discussion     In addition to being unsigned, the title length is in
+        *       network byte order.
+        */
+       unsigned short titleLen;
+       /*      In addition to being unsigned, the description length is in
+        *       network byte order.
+        */
+       unsigned short descriptionLen;
+       /*      In addition to being unsigned, the application name length
+        *       is in network byte order.
+        */
+       unsigned short appNameLen;
+       /*      The variable-sized data of a notification is:
+        *       - Notification name, in UTF-8 encoding, for nameLen bytes.
+        *       - Title, in UTF-8 encoding, for titleLen bytes.
+        *       - Description, in UTF-8 encoding, for descriptionLen bytes.
+        *       - Application name, in UTF-8 encoding, for appNameLen bytes.
+        *       - The MD5/SHA256 checksum of all the data preceding the checksum.
+        */
+       unsigned char data[];
+} ATTRIBUTE_PACKED;
+
+/*!    @defined        GrowlEnabledKey
+ *     @abstract       Preference key controlling whether Growl is enabled.
+ *     @discussion     If this is false, then when GrowlHelperApp is launched to open
+ *      a Growl registration dictionary file, GrowlHelperApp will quit when it has
+ *      finished processing the file instead of listening for notifications.
+ */
+#define GrowlEnabledKey                                        XSTR("GrowlEnabled")
+
+/*!    @defined        GROWL_SCREENSHOT_MODE
+ *     @abstract       Preference and notification key controlling whether to save a screenshot of the notification.
+ *     @discussion     This is for GHA's private usage. If your application puts this
+ *      key into a notification dictionary, GHA will clobber it. This key is only
+ *      allowed in the notification dictionaries GHA passes to displays.
+ *
+ *      If this key contains an object whose boolValue is not NO, the display is
+ *      asked to save a screenshot of the notification to
+ *      ~/Library/Application\ Support/Growl/Screenshots.
+ */
+#define GROWL_SCREENSHOT_MODE                  XSTR("ScreenshotMode")
+
+/*!    @defined        GROWL_APP_LOCATION
+ *     @abstract       The location of this application.
+ *     @discussion     Contains either the POSIX path to the application, or a file-data dictionary (as used by the Dock).
+ *      contains the file's alias record and its pathname.
+ */
+#define GROWL_APP_LOCATION                             XSTR("AppLocation")
+
+/*!    @defined        GROWL_REMOTE_ADDRESS
+ *     @abstract       The address of the host who sent this notification/registration.
+ *     @discussion     Contains an NSData with the address of the remote host who
+ *    sent this notification/registration.
+ */
+#define GROWL_REMOTE_ADDRESS                   XSTR("RemoteAddress")
+
+/*!
+ *     @defined    GROWL_PREFPANE_BUNDLE_IDENTIFIER
+ *     @discussion The bundle identifier for the Growl preference pane.
+ */
+#define GROWL_PREFPANE_BUNDLE_IDENTIFIER               XSTR("com.growl.prefpanel")
+/*!
+ *     @defined    GROWL_HELPERAPP_BUNDLE_IDENTIFIER
+ *     @discussion The bundle identifier for the Growl background application (GrowlHelperApp).
+ */
+#define GROWL_HELPERAPP_BUNDLE_IDENTIFIER      XSTR("com.Growl.GrowlHelperApp")
+
+/*!
+ *     @defined    GROWL_PREFPANE_NAME
+ *     @discussion The file name of the Growl preference pane.
+ */
+#define GROWL_PREFPANE_NAME                                            XSTR("Growl.prefPane")
+#define PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY  XSTR("PreferencePanes")
+#define PREFERENCE_PANE_EXTENSION                              XSTR("prefPane")
+
+//plug-in bundle filename extensions
+#define GROWL_PLUGIN_EXTENSION                  XSTR("growlPlugin")
+#define GROWL_PATHWAY_EXTENSION                 XSTR("growlPathway")
+#define GROWL_VIEW_EXTENSION                                   XSTR("growlView")
+#define GROWL_STYLE_EXTENSION                                  XSTR("growlStyle")
+
+/* --- These following macros are intended for plug-ins --- */
+
+/*!    @function    SYNCHRONIZE_GROWL_PREFS
+ *     @abstract    Synchronizes Growl prefs so they're up-to-date.
+ *     @discussion  This macro is intended for use by GrowlHelperApp and by
+ *      plug-ins (when the prefpane is selected).
+ */
+#define SYNCHRONIZE_GROWL_PREFS() CFPreferencesAppSynchronize(CFSTR("com.Growl.GrowlHelperApp"))
+
+/*!    @function    UPDATE_GROWL_PREFS
+ *     @abstract    Tells GrowlHelperApp to update its prefs.
+ *     @discussion  This macro is intended for use by plug-ins.
+ *      It sends a notification to tell GrowlHelperApp to update its preferences.
+ */
+#define UPDATE_GROWL_PREFS() do { \
+       SYNCHRONIZE_GROWL_PREFS(); \
+       CFStringRef _key = CFSTR("pid"); \
+       int pid = getpid(); \
+       CFNumberRef _value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); \
+       CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&_key, (const void **)&_value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); \
+       CFRelease(_value); \
+       CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), \
+                                                                                CFSTR("GrowlPreferencesChanged"), \
+                                                                                CFSTR("GrowlUserDefaults"), \
+                                                                                userInfo, false); \
+       CFRelease(userInfo); \
+       } while(0)
+
+/*!    @function    READ_GROWL_PREF_VALUE
+ *     @abstract    Reads the given pref value from the plug-in's preferences.
+ *     @discussion  This macro is intended for use by plug-ins. It reads the value for the
+ *      given key from the plug-in's preferences (which are stored in a dictionary inside of
+ *      GrowlHelperApp's prefs).
+ *     @param  key     The preference key to read the value of.
+ *     @param  domain  The bundle ID of the plug-in.
+ *     @param  type    The type of the result expected.
+ *     @param  result  A pointer to an id. Set to the value if exists, left unchanged if not.
+ *
+ *      If the value is set, you are responsible for releasing it.
+ */
+#define READ_GROWL_PREF_VALUE(key, domain, type, result) do {\
+       CFDictionaryRef prefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \
+                                                                                                                                               CFSTR("com.Growl.GrowlHelperApp")); \
+       if (prefs) {\
+               if (CFDictionaryContainsKey(prefs, key)) {\
+                       *result = (type)CFDictionaryGetValue(prefs, key); \
+                       CFRetain(*result); \
+               } \
+               CFRelease(prefs); } \
+       } while(0)
+
+/*!    @function    WRITE_GROWL_PREF_VALUE
+ *     @abstract    Writes the given pref value to the plug-in's preferences.
+ *     @discussion  This macro is intended for use by plug-ins. It writes the given
+ *      value to the plug-in's preferences.
+ *     @param  key     The preference key to write the value of.
+ *     @param  value   The value to write to the preferences. It should be either a
+ *      CoreFoundation type or toll-free bridged with one.
+ *     @param  domain  The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_VALUE(key, value, domain) do {\
+       CFDictionaryRef staticPrefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \
+                                                                                                                                                        CFSTR("com.Growl.GrowlHelperApp")); \
+       CFMutableDictionaryRef prefs; \
+       if (staticPrefs == NULL) {\
+               prefs = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); \
+       } else {\
+               prefs = CFDictionaryCreateMutableCopy(NULL, 0, staticPrefs); \
+               CFRelease(staticPrefs); \
+       }\
+       CFDictionarySetValue(prefs, key, value); \
+       CFPreferencesSetAppValue((CFStringRef)domain, prefs, CFSTR("com.Growl.GrowlHelperApp")); \
+       CFRelease(prefs); } while(0)
+
+/*!    @function    READ_GROWL_PREF_BOOL
+ *     @abstract    Reads the given Boolean from the plug-in's preferences.
+ *     @discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *      use with Booleans.
+ *     @param  key     The preference key to read the Boolean from.
+ *     @param  domain  The bundle ID of the plug-in.
+ *     @param  result  A pointer to a Boolean type. Left unchanged if the value doesn't exist.
+ */
+#define READ_GROWL_PREF_BOOL(key, domain, result) do {\
+       CFBooleanRef boolValue = NULL; \
+       READ_GROWL_PREF_VALUE(key, domain, CFBooleanRef, &boolValue); \
+       if (boolValue) {\
+               *result = CFBooleanGetValue(boolValue); \
+               CFRelease(boolValue); \
+       } } while(0)
+
+/*!    @function    WRITE_GROWL_PREF_BOOL
+ *     @abstract    Writes the given Boolean to the plug-in's preferences.
+ *     @discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *      use with Booleans.
+ *     @param  key     The preference key to write the Boolean for.
+ *     @param  value   The Boolean value to write to the preferences.
+ *     @param  domain  The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_BOOL(key, value, domain) do {\
+       WRITE_GROWL_PREF_VALUE(key, value ? kCFBooleanTrue : kCFBooleanFalse, domain); } while(0)
+
+/*!    @function    READ_GROWL_PREF_INT
+ *     @abstract    Reads the given integer from the plug-in's preferences.
+ *     @discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *      use with integers.
+ *     @param  key     The preference key to read the integer from.
+ *     @param  domain  The bundle ID of the plug-in.
+ *     @param  result  A pointer to an integer. Leaves unchanged if the value doesn't exist.
+ */
+#define READ_GROWL_PREF_INT(key, domain, result) do {\
+       CFNumberRef intValue = NULL; \
+       READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &intValue); \
+       if (intValue) {\
+               CFNumberGetValue(intValue, kCFNumberIntType, result); \
+               CFRelease(intValue); \
+       } } while(0)
+
+/*!    @function    WRITE_GROWL_PREF_INT
+ *     @abstract    Writes the given integer to the plug-in's preferences.
+ *     @discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *      use with integers.
+ *     @param  key     The preference key to write the integer for.
+ *     @param  value   The integer value to write to the preferences.
+ *     @param  domain  The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_INT(key, value, domain) do {\
+       CFNumberRef intValue = CFNumberCreate(NULL, kCFNumberIntType, &value); \
+       WRITE_GROWL_PREF_VALUE(key, intValue, domain); \
+       CFRelease(intValue); } while(0)
+
+/*!    @function    READ_GROWL_PREF_FLOAT
+ *     @abstract    Reads the given float from the plug-in's preferences.
+ *     @discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *      use with floats.
+ *     @param  key     The preference key to read the float from.
+ *     @param  domain  The bundle ID of the plug-in.
+ *     @param  result  A pointer to a float. Leaves unchanged if the value doesn't exist.
+ */
+#ifdef __LP64__
+#define READ_GROWL_PREF_FLOAT(key, domain, result) do {\
+       CFNumberRef floatValue = NULL; \
+       READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &floatValue); \
+       if (floatValue) {\
+               CFNumberGetValue(floatValue, kCFNumberCGFloatType, result); \
+               CFRelease(floatValue); \
+       } } while(0)
+#else
+#define READ_GROWL_PREF_FLOAT(key, domain, result) do {\
+       CFNumberRef floatValue = NULL; \
+       READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &floatValue); \
+       if (floatValue) {\
+               CFNumberGetValue(floatValue, kCFNumberFloatType, result); \
+               CFRelease(floatValue); \
+       } } while(0)
+#endif
+
+/*!    @function    WRITE_GROWL_PREF_FLOAT
+ *     @abstract    Writes the given float to the plug-in's preferences.
+ *     @discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *      use with floats.
+ *     @param  key     The preference key to write the float for.
+ *     @param  value   The float value to write to the preferences.
+ *     @param  domain  The bundle ID of the plug-in.
+ */
+#ifdef __LP64__
+#define WRITE_GROWL_PREF_FLOAT(key, value, domain) do {\
+       CFNumberRef floatValue = CFNumberCreate(NULL, kCFNumberCGFloatType, &value); \
+       WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \
+       CFRelease(floatValue); } while(0)
+#else
+#define WRITE_GROWL_PREF_FLOAT(key, value, domain) do {\
+       CFNumberRef floatValue = CFNumberCreate(NULL, kCFNumberFloatType, &value); \
+       WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \
+       CFRelease(floatValue); } while(0)
+#endif
+
+
+/*!    @defined        GROWL_CLOSE_ALL_NOTIFICATIONS
+ *     @abstract       Notification to close all Growl notifications
+ *     @discussion     Should be posted to the default notification center when a close widget is option+clicked.
+ *    All notifications should close in response. 
+ */
+#define GROWL_CLOSE_ALL_NOTIFICATIONS XSTR("GrowlCloseAllNotifications")
+
+#pragma mark Small utilities
+
+/*!
+ * @defined FLOAT_EQ(x,y)
+ * @abstract Compares two floats.
+ */
+#define FLOAT_EQ(x,y) (((y - FLT_EPSILON) < x) && (x < (y + FLT_EPSILON)))
+
+#endif //ndef _GROWL_GROWLDEFINESINTERNAL_H
diff --git a/src/platform/DARWIN/growlagent/GrowlPathway.h b/src/platform/DARWIN/growlagent/GrowlPathway.h
new file mode 100644 (file)
index 0000000..e0a7a93
--- /dev/null
@@ -0,0 +1,23 @@
+//
+//  GrowlNotificationServer.h
+//  Growl
+//
+//  Created by Ingmar Stein on 15.11.04.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#import <Foundation/Foundation.h>
+
+@protocol GrowlNotificationProtocol
+- (oneway void) registerApplicationWithDictionary:(bycopy NSDictionary *)dict;
+- (oneway void) postNotificationWithDictionary:(bycopy NSDictionary *)notification;
+- (bycopy NSString *) growlVersion;
+@end
+
+@class GrowlApplicationController;
+
+@interface GrowlPathway : NSObject <GrowlNotificationProtocol> {
+}
+
+@end
diff --git a/src/platform/DARWIN/growlagent/Makefile.in b/src/platform/DARWIN/growlagent/Makefile.in
new file mode 100644 (file)
index 0000000..3d71b6c
--- /dev/null
@@ -0,0 +1,33 @@
+srcdir=@srcdir@
+include @TOP_OBJDIR@/src/config/Makefile.config
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+
+OBJS=main.o CFGrowlAdditions.o
+
+all: growlagent-openafs
+
+growlagent-openafs: ${OBJS}
+       ${CC} ${CFLAGS} -o $@ ${OBJS} -framework Security -framework AppKit -framework CoreFoundation
+
+CFGrowlAdditions.o: CFGrowlAdditions.h
+main.o: GrowlDefines.h GrowlPathway.h CFGrowlAdditions.h
+
+clean:
+       $(RM) -f *.o growlagent-openafs
+
+install:
+
+dest: \
+       ${DEST}/tools/growlagent-openafs \
+       ${DEST}/tools/._growlagent-openafs
+
+${DEST}/tools/growlagent-openafs: growlagent-openafs
+       ${INSTALL} $? $@
+${DEST}/tools/._growlagent-openafs: ${srcdir}/._growlagent-openafs
+       ${INSTALL} $? $@
+
+include ../../../config/Makefile.version
+
diff --git a/src/platform/DARWIN/growlagent/main.m b/src/platform/DARWIN/growlagent/main.m
new file mode 100644 (file)
index 0000000..1d256da
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ Copyright (c) The Growl Project, 2004-2005
+ All rights reserved.
+
+
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Growl nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+#import <Foundation/Foundation.h>
+#import "GrowlDefines.h"
+#import "GrowlPathway.h"
+#include "CFGrowlAdditions.h"
+
+#include <mach-o/dyld.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sys/param.h>
+
+#define VMON_SOCKET     2106
+
+#define STORESTR "store$"
+#define FETCHSTR "fetch$"
+#define WARNSTR  "warn$"  
+#define STOREOFFT 6
+#define FETCHOFFT 6
+#define WARNOFFT  6
+
+#define NOTIFICATION_NAME CFSTR("OpenAFS Venus Monitor")
+#define APPLICATION_NAME CFSTR("afshelper")
+
+#define STRINGIFY(x) STRINGIFY2(x)
+#define STRINGIFY2(x) #x
+
+static void notificationDismissed(CFNotificationCenterRef center,
+                                                                 void *observer,
+                                                                 CFStringRef name,
+                                                                 const void *object,
+                                                                 CFDictionaryRef userInfo) {
+#pragma unused(center,observer,name,object,userInfo)
+       /*CFRunLoopStop(CFRunLoopGetCurrent());*/
+}
+
+void getPath(char **selfPathPtr) 
+{
+       uint32_t selfPathSize = MAXPATHLEN;
+       if(!(*selfPathPtr = malloc(selfPathSize)))
+       {
+               exit(-1);
+       }
+       if(_NSGetExecutablePath(*selfPathPtr, &selfPathSize) == -1)
+       {
+               // Try reallocating selfPath with the size returned by the func
+               if(!(*selfPathPtr = realloc(*selfPathPtr, selfPathSize + 1)))
+               {
+                       NSLog(@"Could not allocate memory to hold executable path.");
+                       exit(-1);
+               }
+               if(_NSGetExecutablePath(*selfPathPtr, &selfPathSize) != 0)
+               {
+                       NSLog(@"Could not get executable path.");
+                       exit(-1);
+               }
+       }
+}
+
+static void
+MyTransmit(CFNotificationCenterRef distCenter, CFDictionaryRef registerInfo, CFMutableDictionaryRef notificationInfo)
+{
+       NSConnection *connection = [NSConnection connectionWithRegisteredName:@"GrowlApplicationBridgePathway" host:nil];
+       if (connection) {
+               //Post to Growl via GrowlApplicationBridgePathway
+               @try {
+                       NSDistantObject *theProxy = [connection rootProxy];
+                       [theProxy setProtocolForProxy:@protocol(GrowlNotificationProtocol)];
+                       id<GrowlNotificationProtocol> growlProxy = (id)theProxy;
+                       [growlProxy registerApplicationWithDictionary:(NSDictionary *)registerInfo];
+                       [growlProxy postNotificationWithDictionary:(NSDictionary *)notificationInfo];
+               } @catch(NSException *e) {
+                       NSLog(@"exception while sending notification: %@", e);
+               }
+       } else {
+               //Post to Growl via NSDistributedNotificationCenter
+               NSLog(@"could not find local GrowlApplicationBridgePathway, falling back to NSDNC");
+               CFNotificationCenterPostNotificationWithOptions(distCenter, (CFStringRef)GROWL_APP_REGISTRATION, NULL, registerInfo, kCFNotificationPostToAllSessions);
+               CFNotificationCenterPostNotificationWithOptions(distCenter, (CFStringRef)GROWL_NOTIFICATION, NULL, notificationInfo, kCFNotificationPostToAllSessions);
+       }
+}
+
+static void
+BuildNotificationInfo(char *recvbuf, CFNotificationCenterRef dcref, CFDictionaryRef regref, CFDataRef icon)
+{
+       int priority;
+       CFNumberRef priorityNumber;
+       CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+       CFStringRef clickContext = CFUUIDCreateString(kCFAllocatorDefault, uuid);
+       CFMutableDictionaryRef notificationInfo = CFDictionaryCreateMutable(kCFAllocatorDefault ,9, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_NAME, NOTIFICATION_NAME);
+       CFDictionarySetValue(notificationInfo, GROWL_APP_NAME, APPLICATION_NAME);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_STICKY, kCFBooleanFalse);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_ICON, icon);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_CLICK_CONTEXT, clickContext);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_TITLE, CFSTR("OpenAFS")/*title*/);
+       //CFRelease(title);
+#if 0
+       /* if fetching ever provides more data we could use this */
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_PROGRESS, progressNumber);
+#endif
+       if (!strncmp(recvbuf, FETCHSTR, sizeof(FETCHSTR)-1)) {
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_DESCRIPTION, CFStringCreateWithCString(kCFAllocatorDefault, recvbuf+FETCHOFFT, kCFStringEncodingUTF8));
+               priority = -1;
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_IDENTIFIER, CFSTR("AFSFetch"));
+       } else if (!strncmp(recvbuf, STORESTR, sizeof(STORESTR)-1)) {
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_DESCRIPTION, CFStringCreateWithCString(kCFAllocatorDefault, recvbuf+STOREOFFT, kCFStringEncodingUTF8));
+               priority = -1;
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_IDENTIFIER, CFSTR("AFSStore"));
+       } else if (!strncmp(recvbuf, WARNSTR, sizeof(WARNSTR)-1)) {
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_DESCRIPTION, CFStringCreateWithCString(kCFAllocatorDefault, recvbuf+WARNOFFT, kCFStringEncodingUTF8));
+               priority = +1;
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_IDENTIFIER, CFSTR("AFSWarn"));
+       } else {
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_DESCRIPTION, CFStringCreateWithCString(kCFAllocatorDefault, recvbuf, kCFStringEncodingUTF8));
+               priority = -2;
+               CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_IDENTIFIER, CFSTR("AFS"));
+       }
+       priorityNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &priority);
+       CFDictionarySetValue(notificationInfo, GROWL_NOTIFICATION_PRIORITY, priorityNumber);
+       CFRelease(priorityNumber);      
+       CFRelease(clickContext);
+       MyTransmit(dcref, regref, notificationInfo);
+}
+
+struct GrowlCBContext {
+       CFNotificationCenterRef dcref;
+       CFDictionaryRef regref;
+       CFDataRef icon;
+};
+
+static void MySocketReadCallBack(CFSocketRef socket, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
+{
+       struct GrowlCBContext* callbackInfo = (struct GrowlCBContext * )info;
+       uint8_t recvBuffer[1024];
+       int result;
+       int recvSocket = CFSocketGetNative( socket );
+       if (!info) return;
+       if ( callbackType != kCFSocketReadCallBack ) return;
+
+       result = recvfrom( recvSocket, recvBuffer, sizeof(recvBuffer), 0, NULL, NULL );
+       if (result) {
+               recvBuffer[result-1] = '\0';
+               BuildNotificationInfo((char *)recvBuffer, callbackInfo->dcref, callbackInfo->regref, callbackInfo->icon);
+       }
+}
+
+int main(int argc, const char **argv) {
+       BOOL         wait = NO;
+       int          code = EXIT_SUCCESS;
+       CFDataRef icon = NULL;
+       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+       // get icon data for application name
+       char *selfPath;
+       getPath(&selfPath);
+       CFStringRef appPath = CFStringCreateWithCString(kCFAllocatorDefault, selfPath, kCFStringEncodingUTF8);
+       NSURL *appURL = [NSURL fileURLWithPath:(NSString *)appPath];
+       icon = (CFDataRef)copyIconDataForURL(appURL);
+       free(selfPath);
+
+       // Register with Growl
+       CFStringRef name = NOTIFICATION_NAME;
+       CFArrayRef defaultAndAllNotifications = CFArrayCreate(kCFAllocatorDefault, (const void **)&name, 1, &kCFTypeArrayCallBacks);
+       CFTypeRef registerKeys[4] = {
+               GROWL_APP_NAME,
+               GROWL_NOTIFICATIONS_ALL,
+               GROWL_NOTIFICATIONS_DEFAULT,
+               GROWL_APP_ICON
+       };
+       CFTypeRef registerValues[4] = {
+               APPLICATION_NAME,
+               defaultAndAllNotifications,
+               defaultAndAllNotifications,
+               icon
+       };
+       CFDictionaryRef registerInfo = CFDictionaryCreate(kCFAllocatorDefault, registerKeys, registerValues, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       CFRelease(defaultAndAllNotifications);
+       CFRelease(icon);
+
+       CFNotificationCenterRef distCenter;
+       {
+               distCenter = CFNotificationCenterGetDistributedCenter();
+               if (wait) {
+                       CFMutableStringRef notificationName = CFStringCreateMutable(kCFAllocatorDefault, 0);
+                       CFStringRef applicationName1 = APPLICATION_NAME;
+                       CFStringAppend(notificationName, applicationName1);
+                       CFStringAppend(notificationName, (CFStringRef)GROWL_NOTIFICATION_CLICKED);
+                       CFNotificationCenterAddObserver(distCenter, "openafsgrowl", notificationDismissed, notificationName, NULL, CFNotificationSuspensionBehaviorCoalesce);
+                       CFStringReplaceAll(notificationName, applicationName1);
+                       CFStringAppend(notificationName, (CFStringRef)GROWL_NOTIFICATION_TIMED_OUT);
+                       CFNotificationCenterAddObserver(distCenter, "openafsgrowl", notificationDismissed, notificationName, NULL, CFNotificationSuspensionBehaviorCoalesce);
+                       CFRelease(notificationName);
+               }
+       }
+       struct sockaddr_in sin = { .sin_family = AF_INET, .sin_port = htons(VMON_SOCKET), .sin_addr.s_addr = INADDR_ANY };
+       CFDataRef theAddress = CFDataCreateWithBytesNoCopy(NULL, (UInt8 *)&sin, sizeof(struct sockaddr_in), kCFAllocatorNull);
+       CFSocketSignature MarinerSignature = { PF_INET, SOCK_DGRAM, IPPROTO_UDP, theAddress };
+       struct GrowlCBContext growlContext = {distCenter, registerInfo, icon};
+       CFSocketContext MarinerSocketContext = {0, (void *)&growlContext, NULL, NULL, NULL };
+       
+       CFSocketRef MarinerSocket = CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &MarinerSignature, kCFSocketReadCallBack, &MySocketReadCallBack, &MarinerSocketContext);
+       
+       CFSocketSetSocketFlags(MarinerSocket, kCFSocketCloseOnInvalidate|kCFSocketAutomaticallyReenableReadCallBack);
+       
+       CFRunLoopSourceRef MarinerRunLoopSource = CFSocketCreateRunLoopSource(NULL, MarinerSocket, 0);
+       CFRunLoopAddSource(CFRunLoopGetCurrent(), MarinerRunLoopSource, kCFRunLoopCommonModes);
+
+       /* Run the run loop until it is manually cancelled */
+       CFRunLoopRun();
+
+       CFRelease(registerInfo);
+       CFRelease(MarinerRunLoopSource);
+       CFSocketInvalidate(MarinerSocket);
+       CFRelease(MarinerSocket);
+       /* CFRelease(notificationInfo); */
+       [pool release];
+
+       return code;
+}