// // CFGrowlAdditions.c // Growl // // Created by Peter Hosey 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 #include #include #include #include #include "CFGrowlAdditions.h" #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif extern Boolean CFStringGetFileSystemRepresentation() __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 (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; }