| @@ -1,439 +1,469 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
| Copyright 2004-7 by Raw Material Software ltd. | |||||
| ------------------------------------------------------------------------------ | |||||
| JUCE can be redistributed and/or modified under the terms of the | |||||
| GNU General Public License, as published by the Free Software Foundation; | |||||
| either version 2 of the License, or (at your option) any later version. | |||||
| JUCE is distributed in the hope that it will be useful, | |||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| GNU General Public License for more details. | |||||
| You should have received a copy of the GNU General Public License | |||||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||||
| Boston, MA 02111-1307 USA | |||||
| ------------------------------------------------------------------------------ | |||||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||||
| more information. | |||||
| ============================================================================== | |||||
| */ | |||||
| #include "juce_mac_NativeHeaders.h" | |||||
| #include <IOKit/IOKitLib.h> | |||||
| #include <IOKit/network/IOEthernetInterface.h> | |||||
| #include <IOKit/network/IONetworkInterface.h> | |||||
| #include <IOKit/network/IOEthernetController.h> | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../src/juce_core/text/juce_String.h" | |||||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||||
| #include "../../../src/juce_core/basics/juce_Logger.h" | |||||
| #include "../../../src/juce_core/threads/juce_ScopedLock.h" | |||||
| #include "../../../src/juce_core/threads/juce_WaitableEvent.h" | |||||
| #include "../../../src/juce_core/threads/juce_Thread.h" | |||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||||
| //#include "juce_mac_HTTPStream.h" | |||||
| //============================================================================== | |||||
| static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() | |||||
| { | |||||
| mach_port_t masterPort; | |||||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||||
| { | |||||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||||
| if (dict != 0) | |||||
| { | |||||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||||
| 0, | |||||
| &kCFTypeDictionaryKeyCallBacks, | |||||
| &kCFTypeDictionaryValueCallBacks); | |||||
| if (propDict != 0) | |||||
| { | |||||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||||
| CFRelease (propDict); | |||||
| } | |||||
| } | |||||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | |||||
| int numResults = 0; | |||||
| io_iterator_t it; | |||||
| if (GetEthernetIterator (&it)) | |||||
| { | |||||
| io_object_t i; | |||||
| while ((i = IOIteratorNext (it)) != 0) | |||||
| { | |||||
| io_object_t controller; | |||||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||||
| { | |||||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||||
| CFSTR (kIOMACAddress), | |||||
| kCFAllocatorDefault, | |||||
| 0); | |||||
| if (data != 0) | |||||
| { | |||||
| UInt8 addr [kIOEthernetAddressSize]; | |||||
| zeromem (addr, sizeof (addr)); | |||||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||||
| CFRelease (data); | |||||
| int64 a = 0; | |||||
| for (int i = 6; --i >= 0;) | |||||
| a = (a << 8) | addr[i]; | |||||
| if (! littleEndian) | |||||
| a = (int64) swapByteOrder ((uint64) a); | |||||
| if (numResults < maxNum) | |||||
| { | |||||
| *addresses++ = a; | |||||
| ++numResults; | |||||
| } | |||||
| } | |||||
| IOObjectRelease (controller); | |||||
| } | |||||
| IOObjectRelease (i); | |||||
| } | |||||
| IOObjectRelease (it); | |||||
| } | |||||
| return numResults; | |||||
| } | |||||
| //============================================================================== | |||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| const AutoPool pool; | |||||
| String script; | |||||
| script << "tell application \"Mail\"\r\n" | |||||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||||
| << emailSubject.replace (T("\""), T("\\\"")) | |||||
| << "\", content:\"" | |||||
| << bodyText.replace (T("\""), T("\\\"")) | |||||
| << "\" & return & return}\r\n" | |||||
| "tell newMessage\r\n" | |||||
| "set visible to true\r\n" | |||||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||||
| "make new to recipient at end of to recipients with properties {address:\"" | |||||
| << targetEmailAddress | |||||
| << "\"}\r\n"; | |||||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||||
| { | |||||
| script << "tell content\r\n" | |||||
| "make new attachment with properties {file name:\"" | |||||
| << filesToAttach[i].replace (T("\""), T("\\\"")) | |||||
| << "\"} at after the last paragraph\r\n" | |||||
| "end tell\r\n"; | |||||
| } | |||||
| script << "end tell\r\n" | |||||
| "end tell\r\n"; | |||||
| NSAppleScript* s = [[NSAppleScript alloc] | |||||
| initWithSource: [NSString stringWithUTF8String: (const char*) script.toUTF8()]]; | |||||
| NSDictionary* error = 0; | |||||
| const bool ok = [s executeAndReturnError: &error] != nil; | |||||
| [s release]; | |||||
| return ok; | |||||
| } | |||||
| //============================================================================== | |||||
| END_JUCE_NAMESPACE | |||||
| using namespace JUCE_NAMESPACE; | |||||
| @interface JuceURLConnection : NSInputStream | |||||
| { | |||||
| NSURLConnection* connection; | |||||
| NSMutableData* data; | |||||
| NSThread* runLoopThread; | |||||
| NSRunLoop* runLoop; | |||||
| bool initialised, hasFailed, hasFinished; | |||||
| int position; | |||||
| NSLock* dataLock; | |||||
| } | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req; | |||||
| - (void) dealloc; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection; | |||||
| - (BOOL) isOpen; | |||||
| - (int) read: (char*) dest numBytes: (int) num; | |||||
| - (int) readPosition; | |||||
| - (void) runThread: (id) dummyParam; | |||||
| @end | |||||
| @implementation JuceURLConnection | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req | |||||
| { | |||||
| [super init]; | |||||
| data = [[NSMutableData data] retain]; | |||||
| dataLock = [[NSLock alloc] init]; | |||||
| connection = 0; | |||||
| initialised = false; | |||||
| hasFailed = false; | |||||
| hasFinished = false; | |||||
| runLoopThread = [[NSThread alloc] initWithTarget: self | |||||
| selector: @selector (runThread:) | |||||
| object: req]; | |||||
| [runLoopThread start]; | |||||
| while (! [runLoopThread isExecuting]) | |||||
| Thread::sleep (1); | |||||
| while ([runLoopThread isExecuting] && ! initialised) | |||||
| Thread::sleep (1); | |||||
| return self; | |||||
| } | |||||
| - (void) dealloc | |||||
| { | |||||
| [connection cancel]; | |||||
| [runLoopThread cancel]; | |||||
| while ([runLoopThread isExecuting]) | |||||
| [NSThread sleepForTimeInterval: 0.001]; | |||||
| [runLoopThread release]; | |||||
| [connection release]; | |||||
| [data release]; | |||||
| [dataLock release]; | |||||
| [super dealloc]; | |||||
| } | |||||
| - (void) runThread: (id) request | |||||
| { | |||||
| AutoPool pool; | |||||
| runLoop = [NSRunLoop currentRunLoop]; | |||||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||||
| delegate: self | |||||
| startImmediately: YES]; | |||||
| if (connection == nil) | |||||
| { | |||||
| hasFailed = true; | |||||
| return; | |||||
| } | |||||
| initialised = true; | |||||
| while (! ([[NSThread currentThread] isCancelled] || hasFailed || hasFinished)) | |||||
| { | |||||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||||
| } | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data setLength: 0]; | |||||
| [dataLock unlock]; | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error | |||||
| { | |||||
| NSLog ([error description]); | |||||
| hasFailed = true; | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) newData | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data appendData: newData]; | |||||
| [dataLock unlock]; | |||||
| } | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection | |||||
| { | |||||
| hasFinished = true; | |||||
| } | |||||
| - (BOOL) isOpen | |||||
| { | |||||
| return connection != 0 && ! hasFailed; | |||||
| } | |||||
| - (int) readPosition | |||||
| { | |||||
| return position; | |||||
| } | |||||
| - (int) read: (char*) dest numBytes: (int) numNeeded | |||||
| { | |||||
| int numDone = 0; | |||||
| while (numNeeded > 0) | |||||
| { | |||||
| int available = jmin (numNeeded, [data length]); | |||||
| if (available > 0) | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data getBytes: dest length: available]; | |||||
| [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; | |||||
| [dataLock unlock]; | |||||
| numDone += available; | |||||
| numNeeded -= available; | |||||
| dest += available; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (hasFailed || hasFinished) | |||||
| break; | |||||
| Thread::sleep (1); | |||||
| } | |||||
| } | |||||
| position += numDone; | |||||
| return numDone; | |||||
| } | |||||
| @end | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| bool juce_isOnLine() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void* juce_openInternetFile (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| AutoPool pool; | |||||
| NSMutableURLRequest* req = [NSMutableURLRequest | |||||
| requestWithURL: [NSURL URLWithString: juceStringToNS (url)] | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||||
| if (req == nil) | |||||
| return 0; | |||||
| [req setHTTPMethod: isPost ? @"POST" : @"GET"]; | |||||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||||
| StringArray headerLines; | |||||
| headerLines.addLines (headers); | |||||
| headerLines.removeEmptyStrings (true); | |||||
| for (int i = 0; i < headerLines.size(); ++i) | |||||
| { | |||||
| const String key (headerLines[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| const String value (headerLines[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| if (key.isNotEmpty() && value.isNotEmpty()) | |||||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | |||||
| } | |||||
| if (isPost && postData.getSize() > 0) | |||||
| { | |||||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||||
| length: postData.getSize()]]; | |||||
| } | |||||
| JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req]; | |||||
| if ([s isOpen]) | |||||
| return s; | |||||
| delete s; | |||||
| return 0; | |||||
| } | |||||
| void juce_closeInternetFile (void* handle) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| AutoPool pool; | |||||
| [s release]; | |||||
| } | |||||
| } | |||||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| AutoPool pool; | |||||
| return [s read: (char*) buffer numBytes: bytesToRead]; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| return [s readPosition]; | |||||
| return 0; | |||||
| } | |||||
| END_JUCE_NAMESPACE | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
| Copyright 2004-7 by Raw Material Software ltd. | |||||
| ------------------------------------------------------------------------------ | |||||
| JUCE can be redistributed and/or modified under the terms of the | |||||
| GNU General Public License, as published by the Free Software Foundation; | |||||
| either version 2 of the License, or (at your option) any later version. | |||||
| JUCE is distributed in the hope that it will be useful, | |||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| GNU General Public License for more details. | |||||
| You should have received a copy of the GNU General Public License | |||||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||||
| Boston, MA 02111-1307 USA | |||||
| ------------------------------------------------------------------------------ | |||||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||||
| more information. | |||||
| ============================================================================== | |||||
| */ | |||||
| #include "juce_mac_NativeHeaders.h" | |||||
| #include <IOKit/IOKitLib.h> | |||||
| #include <IOKit/network/IOEthernetInterface.h> | |||||
| #include <IOKit/network/IONetworkInterface.h> | |||||
| #include <IOKit/network/IOEthernetController.h> | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../src/juce_core/text/juce_String.h" | |||||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||||
| #include "../../../src/juce_core/basics/juce_Logger.h" | |||||
| #include "../../../src/juce_core/threads/juce_ScopedLock.h" | |||||
| #include "../../../src/juce_core/threads/juce_WaitableEvent.h" | |||||
| #include "../../../src/juce_core/threads/juce_Thread.h" | |||||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||||
| //#include "juce_mac_HTTPStream.h" | |||||
| //============================================================================== | |||||
| static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() | |||||
| { | |||||
| mach_port_t masterPort; | |||||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||||
| { | |||||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||||
| if (dict != 0) | |||||
| { | |||||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||||
| 0, | |||||
| &kCFTypeDictionaryKeyCallBacks, | |||||
| &kCFTypeDictionaryValueCallBacks); | |||||
| if (propDict != 0) | |||||
| { | |||||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||||
| CFRelease (propDict); | |||||
| } | |||||
| } | |||||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||||
| { | |||||
| int numResults = 0; | |||||
| io_iterator_t it; | |||||
| if (GetEthernetIterator (&it)) | |||||
| { | |||||
| io_object_t i; | |||||
| while ((i = IOIteratorNext (it)) != 0) | |||||
| { | |||||
| io_object_t controller; | |||||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||||
| { | |||||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||||
| CFSTR (kIOMACAddress), | |||||
| kCFAllocatorDefault, | |||||
| 0); | |||||
| if (data != 0) | |||||
| { | |||||
| UInt8 addr [kIOEthernetAddressSize]; | |||||
| zeromem (addr, sizeof (addr)); | |||||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||||
| CFRelease (data); | |||||
| int64 a = 0; | |||||
| for (int i = 6; --i >= 0;) | |||||
| a = (a << 8) | addr[i]; | |||||
| if (! littleEndian) | |||||
| a = (int64) swapByteOrder ((uint64) a); | |||||
| if (numResults < maxNum) | |||||
| { | |||||
| *addresses++ = a; | |||||
| ++numResults; | |||||
| } | |||||
| } | |||||
| IOObjectRelease (controller); | |||||
| } | |||||
| IOObjectRelease (i); | |||||
| } | |||||
| IOObjectRelease (it); | |||||
| } | |||||
| return numResults; | |||||
| } | |||||
| //============================================================================== | |||||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
| const String& emailSubject, | |||||
| const String& bodyText, | |||||
| const StringArray& filesToAttach) | |||||
| { | |||||
| const AutoPool pool; | |||||
| String script; | |||||
| script << "tell application \"Mail\"\r\n" | |||||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||||
| << emailSubject.replace (T("\""), T("\\\"")) | |||||
| << "\", content:\"" | |||||
| << bodyText.replace (T("\""), T("\\\"")) | |||||
| << "\" & return & return}\r\n" | |||||
| "tell newMessage\r\n" | |||||
| "set visible to true\r\n" | |||||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||||
| "make new to recipient at end of to recipients with properties {address:\"" | |||||
| << targetEmailAddress | |||||
| << "\"}\r\n"; | |||||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||||
| { | |||||
| script << "tell content\r\n" | |||||
| "make new attachment with properties {file name:\"" | |||||
| << filesToAttach[i].replace (T("\""), T("\\\"")) | |||||
| << "\"} at after the last paragraph\r\n" | |||||
| "end tell\r\n"; | |||||
| } | |||||
| script << "end tell\r\n" | |||||
| "end tell\r\n"; | |||||
| NSAppleScript* s = [[NSAppleScript alloc] | |||||
| initWithSource: [NSString stringWithUTF8String: (const char*) script.toUTF8()]]; | |||||
| NSDictionary* error = 0; | |||||
| const bool ok = [s executeAndReturnError: &error] != nil; | |||||
| [s release]; | |||||
| return ok; | |||||
| } | |||||
| //============================================================================== | |||||
| END_JUCE_NAMESPACE | |||||
| using namespace JUCE_NAMESPACE; | |||||
| //============================================================================== | |||||
| @interface JuceURLConnection : NSObject | |||||
| { | |||||
| NSURLRequest* request; | |||||
| NSURLConnection* connection; | |||||
| NSMutableData* data; | |||||
| Thread* runLoopThread; | |||||
| bool initialised, hasFailed, hasFinished; | |||||
| int position; | |||||
| NSLock* dataLock; | |||||
| } | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req withCallback: (URL::OpenStreamProgressCallback*) callback withContext: (void*) context; | |||||
| - (void) dealloc; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection; | |||||
| - (BOOL) isOpen; | |||||
| - (int) read: (char*) dest numBytes: (int) num; | |||||
| - (int) readPosition; | |||||
| - (void) stop; | |||||
| - (void) createConnection; | |||||
| @end | |||||
| class JuceURLConnectionMessageThread : public Thread | |||||
| { | |||||
| JuceURLConnection* owner; | |||||
| public: | |||||
| JuceURLConnectionMessageThread (JuceURLConnection* owner_) | |||||
| : Thread (T("http connection")), | |||||
| owner (owner_) | |||||
| { | |||||
| startThread(); | |||||
| } | |||||
| ~JuceURLConnectionMessageThread() | |||||
| { | |||||
| stopThread (5000); | |||||
| } | |||||
| void run() | |||||
| { | |||||
| AutoPool pool; | |||||
| [owner createConnection]; | |||||
| while (! threadShouldExit()) | |||||
| { | |||||
| AutoPool pool; | |||||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||||
| } | |||||
| } | |||||
| }; | |||||
| @implementation JuceURLConnection | |||||
| - (JuceURLConnection*) initWithRequest: (NSURLRequest*) req | |||||
| withCallback: (URL::OpenStreamProgressCallback*) callback | |||||
| withContext: (void*) context; | |||||
| { | |||||
| [super init]; | |||||
| request = req; | |||||
| [request retain]; | |||||
| data = [[NSMutableData data] retain]; | |||||
| dataLock = [[NSLock alloc] init]; | |||||
| connection = 0; | |||||
| initialised = false; | |||||
| hasFailed = false; | |||||
| hasFinished = false; | |||||
| runLoopThread = new JuceURLConnectionMessageThread (self); | |||||
| while (runLoopThread->isThreadRunning() && ! initialised) | |||||
| { | |||||
| if (callback != 0) | |||||
| callback (context, -1, [[request HTTPBody] length]); | |||||
| Thread::sleep (1); | |||||
| } | |||||
| return self; | |||||
| } | |||||
| - (void) dealloc | |||||
| { | |||||
| [self stop]; | |||||
| delete runLoopThread; | |||||
| [connection release]; | |||||
| [data release]; | |||||
| [dataLock release]; | |||||
| [request release]; | |||||
| [super dealloc]; | |||||
| } | |||||
| - (void) createConnection | |||||
| { | |||||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||||
| delegate: self]; | |||||
| if (connection == nil) | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data setLength: 0]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error | |||||
| { | |||||
| NSLog ([error description]); | |||||
| hasFailed = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) newData | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data appendData: newData]; | |||||
| [dataLock unlock]; | |||||
| initialised = true; | |||||
| } | |||||
| - (void) connectionDidFinishLoading: (NSURLConnection*) connection | |||||
| { | |||||
| hasFinished = true; | |||||
| initialised = true; | |||||
| runLoopThread->signalThreadShouldExit(); | |||||
| } | |||||
| - (BOOL) isOpen | |||||
| { | |||||
| return connection != 0 && ! hasFailed; | |||||
| } | |||||
| - (int) readPosition | |||||
| { | |||||
| return position; | |||||
| } | |||||
| - (int) read: (char*) dest numBytes: (int) numNeeded | |||||
| { | |||||
| int numDone = 0; | |||||
| while (numNeeded > 0) | |||||
| { | |||||
| int available = jmin (numNeeded, [data length]); | |||||
| if (available > 0) | |||||
| { | |||||
| [dataLock lock]; | |||||
| [data getBytes: dest length: available]; | |||||
| [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; | |||||
| [dataLock unlock]; | |||||
| numDone += available; | |||||
| numNeeded -= available; | |||||
| dest += available; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (hasFailed || hasFinished) | |||||
| break; | |||||
| Thread::sleep (1); | |||||
| } | |||||
| } | |||||
| position += numDone; | |||||
| return numDone; | |||||
| } | |||||
| - (void) stop | |||||
| { | |||||
| [connection cancel]; | |||||
| runLoopThread->stopThread (5000); | |||||
| } | |||||
| @end | |||||
| BEGIN_JUCE_NAMESPACE | |||||
| bool juce_isOnLine() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void* juce_openInternetFile (const String& url, | |||||
| const String& headers, | |||||
| const MemoryBlock& postData, | |||||
| const bool isPost, | |||||
| URL::OpenStreamProgressCallback* callback, | |||||
| void* callbackContext, | |||||
| int timeOutMs) | |||||
| { | |||||
| AutoPool pool; | |||||
| NSMutableURLRequest* req = [NSMutableURLRequest | |||||
| requestWithURL: [NSURL URLWithString: juceStringToNS (url)] | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||||
| if (req == nil) | |||||
| return 0; | |||||
| [req setHTTPMethod: isPost ? @"POST" : @"GET"]; | |||||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||||
| StringArray headerLines; | |||||
| headerLines.addLines (headers); | |||||
| headerLines.removeEmptyStrings (true); | |||||
| for (int i = 0; i < headerLines.size(); ++i) | |||||
| { | |||||
| const String key (headerLines[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| const String value (headerLines[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); | |||||
| if (key.isNotEmpty() && value.isNotEmpty()) | |||||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | |||||
| } | |||||
| if (isPost && postData.getSize() > 0) | |||||
| { | |||||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||||
| length: postData.getSize()]]; | |||||
| } | |||||
| JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req | |||||
| withCallback: callback | |||||
| withContext: callbackContext]; | |||||
| if ([s isOpen]) | |||||
| return s; | |||||
| [s release]; | |||||
| return 0; | |||||
| } | |||||
| void juce_closeInternetFile (void* handle) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| AutoPool pool; | |||||
| [s stop]; | |||||
| [s release]; | |||||
| } | |||||
| } | |||||
| int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| { | |||||
| AutoPool pool; | |||||
| return [s read: (char*) buffer numBytes: bytesToRead]; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int juce_seekInInternetFile (void* handle, int newPosition) | |||||
| { | |||||
| JuceURLConnection* const s = (JuceURLConnection*) handle; | |||||
| if (s != 0) | |||||
| return [s readPosition]; | |||||
| return 0; | |||||
| } | |||||
| END_JUCE_NAMESPACE | |||||