| @@ -21,7 +21,7 @@ package.config["Debug"].defines = { "LINUX=1", "DEBUG=1", "_DEBUG=1" }; | |||
| package.config["Debug"].buildoptions = { "-D_DEBUG -ggdb -Wall" } | |||
| package.config["Release"].defines = { "LINUX=1", "NDEBUG=1" }; | |||
| package.config["Release"].buildoptions = { "-Wall" } | |||
| package.config["Release"].buildoptions = { "-O2 -Wall -fvisibility=hidden" } | |||
| package.includepaths = { | |||
| @@ -44,16 +44,30 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||
| // we'll borrow the mac's socket-based http streaming code.. | |||
| #include "../../macosx/platform_specific_code/juce_mac_HTTPStream.h" | |||
| //============================================================================== | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum) throw() | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| // xxx todo | |||
| return 0; | |||
| } | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| jassertfalse // xxx todo | |||
| return false; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -471,38 +471,38 @@ void juce_createDirectory (const String& fileName) throw() | |||
| void* juce_fileOpen (const String& fileName, bool forWriting) throw() | |||
| { | |||
| const char* const fileNameUTF8 = fileName.toUTF8(); | |||
| const char* mode = "rb"; | |||
| int flags = O_RDONLY; | |||
| if (forWriting) | |||
| { | |||
| if (juce_fileExists (fileName, false)) | |||
| { | |||
| FILE* const f = fopen (fileNameUTF8, "r+b"); | |||
| const int f = open (fileNameUTF8, O_RDWR, 00644); | |||
| if (f != 0) | |||
| fseek (f, 0, SEEK_END); | |||
| if (f != -1) | |||
| lseek (f, 0, SEEK_END); | |||
| return (void*) f; | |||
| } | |||
| else | |||
| { | |||
| mode = "w+b"; | |||
| flags = O_RDWR + O_CREAT; | |||
| } | |||
| } | |||
| return (void*) fopen (fileNameUTF8, mode); | |||
| return (void*) open (fileNameUTF8, flags, 00644); | |||
| } | |||
| void juce_fileClose (void* handle) throw() | |||
| { | |||
| if (handle != 0) | |||
| fclose ((FILE*) handle); | |||
| close ((int) handle); | |||
| } | |||
| int juce_fileRead (void* handle, void* buffer, int size) throw() | |||
| { | |||
| if (handle != 0) | |||
| return fread (buffer, 1, size, (FILE*) handle); | |||
| return read ((int) handle, buffer, size); | |||
| return 0; | |||
| } | |||
| @@ -510,14 +510,14 @@ int juce_fileRead (void* handle, void* buffer, int size) throw() | |||
| int juce_fileWrite (void* handle, const void* buffer, int size) throw() | |||
| { | |||
| if (handle != 0) | |||
| return fwrite (buffer, 1, size, (FILE*) handle); | |||
| return write ((int) handle, buffer, size); | |||
| return 0; | |||
| } | |||
| int64 juce_fileSetPosition (void* handle, int64 pos) throw() | |||
| { | |||
| if (handle != 0 && fseek ((FILE*) handle, pos, SEEK_SET) == 0) | |||
| if (handle != 0 && lseek ((int) handle, pos, SEEK_SET) == pos) | |||
| return pos; | |||
| return -1; | |||
| @@ -526,7 +526,7 @@ int64 juce_fileSetPosition (void* handle, int64 pos) throw() | |||
| int64 juce_fileGetPosition (void* handle) throw() | |||
| { | |||
| if (handle != 0) | |||
| return ftell ((FILE*) handle); | |||
| return lseek ((int) handle, 0, SEEK_CUR); | |||
| else | |||
| return -1; | |||
| } | |||
| @@ -534,7 +534,7 @@ int64 juce_fileGetPosition (void* handle) throw() | |||
| void juce_fileFlush (void* handle) throw() | |||
| { | |||
| if (handle != 0) | |||
| fflush ((FILE*) handle); | |||
| fsync ((int) handle); | |||
| } | |||
| const StringArray juce_getFileSystemRoots() throw() | |||
| @@ -58,7 +58,8 @@ public: | |||
| //============================================================================== | |||
| bool open (const String& url, | |||
| const String& optionalPostText, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost) | |||
| { | |||
| closeSocket(); | |||
| @@ -101,15 +102,14 @@ public: | |||
| if (! proxyURL.startsWithIgnoreCase (T("http://"))) | |||
| proxyURL = String::empty; | |||
| const String requestHeader (createRequestHeader (hostName, hostPath, | |||
| proxyURL, url, | |||
| hostPort, optionalPostText, | |||
| isPost)); | |||
| const MemoryBlock requestHeader (createRequestHeader (hostName, hostPath, | |||
| proxyURL, url, | |||
| hostPort, | |||
| headers, postData, | |||
| isPost)); | |||
| const char* const utf8Header = (const char*) requestHeader.toUTF8(); | |||
| const int headerLen = strlen (utf8Header); | |||
| if (! send (socketHandle, utf8Header, headerLen, 0) == headerLen) | |||
| if (send (socketHandle, requestHeader.getData(), requestHeader.getSize(), 0) | |||
| != requestHeader.getSize()) | |||
| { | |||
| closeSocket(); | |||
| return false; | |||
| @@ -140,7 +140,7 @@ public: | |||
| location = T("http://") + location; | |||
| if (levelsOfRedirection++ < 3) | |||
| return open (location, optionalPostText, isPost); | |||
| return open (location, headers, postData, isPost); | |||
| } | |||
| else | |||
| { | |||
| @@ -191,13 +191,14 @@ private: | |||
| socketHandle = -1; | |||
| } | |||
| const String createRequestHeader (const String& hostName, | |||
| const String& hostPath, | |||
| const String& proxyURL, | |||
| const String& originalURL, | |||
| const int hostPort, | |||
| const String& optionalPostText, | |||
| const bool isPost) | |||
| const MemoryBlock createRequestHeader (const String& hostName, | |||
| const String& hostPath, | |||
| const String& proxyURL, | |||
| const String& originalURL, | |||
| const int hostPort, | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost) | |||
| { | |||
| String header (isPost ? "POST " : "GET "); | |||
| @@ -212,7 +213,7 @@ private: | |||
| int proxyPort; | |||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||
| return String::empty; | |||
| return MemoryBlock(); | |||
| header << originalURL << " HTTP/1.1\r\nHost: " | |||
| << proxyName << ':' << proxyPort; | |||
| @@ -226,20 +227,14 @@ private: | |||
| header << "\r\nUser-Agent: JUCE/" | |||
| << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||
| << "\r\nConnection: Close\r\n"; | |||
| << "\r\nConnection: Close\r\n" | |||
| << headers << "\r\n"; | |||
| if (isPost && optionalPostText.isNotEmpty()) | |||
| { | |||
| const char* const postTextUTF8 = (const char*) optionalPostText.toUTF8(); | |||
| header << "Content-type: application/x-www-form-urlencoded\r\nContent-length: " | |||
| << (int) strlen (postTextUTF8) << "\r\n\r\n" | |||
| << optionalPostText; | |||
| } | |||
| MemoryBlock mb; | |||
| mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||
| mb.append (postData.getData(), postData.getSize()); | |||
| header << "\r\n"; | |||
| //DBG (header); | |||
| return header; | |||
| return mb; | |||
| } | |||
| const String readResponse() | |||
| @@ -341,12 +336,15 @@ bool juce_isOnLine() | |||
| } | |||
| void* juce_openInternetFile (const String& url, | |||
| const String& optionalPostText, | |||
| const bool isPost) | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext) | |||
| { | |||
| JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream(); | |||
| if (s->open (url, optionalPostText, isPost)) | |||
| if (s->open (url, headers, postData, isPost)) | |||
| return s; | |||
| delete s; | |||
| @@ -1,137 +1,191 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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 "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| #include <IOKit/IOKitLib.h> | |||
| #include <IOKit/network/IOEthernetInterface.h> | |||
| #include <IOKit/network/IONetworkInterface.h> | |||
| #include <IOKit/network/IOEthernetController.h> | |||
| #include <Carbon/Carbon.h> | |||
| #include <netdb.h> | |||
| #include <arpa/inet.h> | |||
| #include <netinet/in.h> | |||
| #include <sys/types.h> | |||
| #include <sys/socket.h> | |||
| #include <sys/wait.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/containers/juce_MemoryBlock.h" | |||
| #include "../../../src/juce_core/text/juce_StringArray.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) 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 (numResults < maxNum) | |||
| { | |||
| *addresses++ = a; | |||
| ++numResults; | |||
| } | |||
| } | |||
| IOObjectRelease (controller); | |||
| } | |||
| IOObjectRelease (i); | |||
| } | |||
| IOObjectRelease (it); | |||
| } | |||
| return numResults; | |||
| } | |||
| 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 "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| #include <IOKit/IOKitLib.h> | |||
| #include <IOKit/network/IOEthernetInterface.h> | |||
| #include <IOKit/network/IONetworkInterface.h> | |||
| #include <IOKit/network/IOEthernetController.h> | |||
| #include <Carbon/Carbon.h> | |||
| #include <netdb.h> | |||
| #include <arpa/inet.h> | |||
| #include <netinet/in.h> | |||
| #include <sys/types.h> | |||
| #include <sys/socket.h> | |||
| #include <sys/wait.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/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) | |||
| { | |||
| String script; | |||
| script << "tell application \"Mail\"\r\n" | |||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||
| << emailSubject | |||
| << "\", content:\"" | |||
| << bodyText | |||
| << "\" & 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] | |||
| << "\"} at after the last paragraph\r\n" | |||
| "end tell\r\n"; | |||
| } | |||
| script << "end tell\r\n" | |||
| "end tell\r\n"; | |||
| DBG (script); | |||
| ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); | |||
| OSAID resultID; | |||
| AEDesc source; | |||
| AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); | |||
| OSAError err = OSACompileExecute (comp, &source, | |||
| kOSANullScript, kOSAModeNull, | |||
| &resultID); | |||
| AEDisposeDesc (&source); | |||
| CloseComponent (comp); | |||
| return false; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -38,6 +38,7 @@ | |||
| #include <wininet.h> | |||
| #include <nb30.h> | |||
| #include <iphlpapi.h> | |||
| #include <mapi.h> | |||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| @@ -46,6 +47,8 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "juce_win32_DynamicLibraryLoader.h" | |||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||
| #ifndef INTERNET_FLAG_NEED_FILE | |||
| #define INTERNET_FLAG_NEED_FILE 0x00000010 | |||
| @@ -72,11 +75,16 @@ struct ConnectionAndRequestStruct | |||
| static HINTERNET sessionHandle = 0; | |||
| void* juce_openInternetFile (const String& url, | |||
| const String& postText, | |||
| const bool isPost) | |||
| const String& headers, | |||
| const MemoryBlock& postData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext) | |||
| { | |||
| if (sessionHandle == 0) | |||
| sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); | |||
| sessionHandle = InternetOpen (_T("juce"), | |||
| INTERNET_OPEN_TYPE_PRECONFIG, | |||
| 0, 0, 0); | |||
| if (sessionHandle != 0) | |||
| { | |||
| @@ -92,9 +100,7 @@ void* juce_openInternetFile (const String& url, | |||
| uc.lpszUrlPath = file; | |||
| uc.lpszHostName = server; | |||
| if (InternetCrackUrl (url, 0, | |||
| ICU_ESCAPE | ICU_DECODE, | |||
| &uc)) | |||
| if (InternetCrackUrl (url, 0, ICU_ESCAPE | ICU_DECODE, &uc)) | |||
| { | |||
| const bool isFtp = url.startsWithIgnoreCase (T("ftp:")); | |||
| @@ -102,8 +108,8 @@ void* juce_openInternetFile (const String& url, | |||
| uc.lpszHostName, | |||
| uc.nPort, | |||
| _T(""), _T(""), | |||
| (isFtp) ? INTERNET_SERVICE_FTP | |||
| : INTERNET_SERVICE_HTTP, | |||
| isFtp ? INTERNET_SERVICE_FTP | |||
| : INTERNET_SERVICE_HTTP, | |||
| 0, 0); | |||
| if (connection != 0) | |||
| @@ -129,24 +135,52 @@ void* juce_openInternetFile (const String& url, | |||
| isPost ? _T("POST") | |||
| : _T("GET"), | |||
| uc.lpszUrlPath, | |||
| 0, 0, | |||
| mimeTypes, | |||
| INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0); | |||
| 0, 0, mimeTypes, | |||
| INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, | |||
| 0); | |||
| if (request != 0) | |||
| { | |||
| // (this header is needed to make webservers process a POST request correctly) | |||
| const String hdr ("Content-Type: application/x-www-form-urlencoded"); | |||
| if (HttpSendRequest (request, | |||
| hdr, hdr.length(), | |||
| (void*) (const char*) postText, | |||
| postText.length())) | |||
| INTERNET_BUFFERS buffers; | |||
| zerostruct (buffers); | |||
| buffers.dwStructSize = sizeof (INTERNET_BUFFERS); | |||
| buffers.lpcszHeader = (const char*) headers; | |||
| buffers.dwHeadersLength = headers.length(); | |||
| buffers.dwBufferTotal = (DWORD) postData.getSize(); | |||
| ConnectionAndRequestStruct* result = 0; | |||
| if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) | |||
| { | |||
| ConnectionAndRequestStruct* const result = new ConnectionAndRequestStruct(); | |||
| result->connection = connection; | |||
| result->request = request; | |||
| return result; | |||
| int bytesSent = 0; | |||
| for (;;) | |||
| { | |||
| const int bytesToDo = jmin (1024, postData.getSize() - bytesSent); | |||
| DWORD bytesDone = 0; | |||
| if (bytesToDo > 0 | |||
| && ! InternetWriteFile (request, | |||
| ((const char*) postData.getData()) + bytesSent, | |||
| bytesToDo, &bytesDone)) | |||
| { | |||
| break; | |||
| } | |||
| if (bytesToDo == 0 || (int) bytesDone < bytesToDo) | |||
| { | |||
| result = new ConnectionAndRequestStruct(); | |||
| result->connection = connection; | |||
| result->request = request; | |||
| HttpEndRequest (request, 0, 0, 0); | |||
| return result; | |||
| } | |||
| bytesSent += bytesDone; | |||
| if (callback != 0 && ! callback (callbackContext, bytesSent, postData.getSize())) | |||
| break; | |||
| } | |||
| } | |||
| InternetCloseHandle (request); | |||
| @@ -222,7 +256,8 @@ void juce_closeInternetFile (void* handle) | |||
| } | |||
| } | |||
| static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw() | |||
| //============================================================================== | |||
| static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| int numFound = 0; | |||
| @@ -251,6 +286,9 @@ static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw( | |||
| for (unsigned int i = 0; i < adapter->AddressLength; ++i) | |||
| mac = (mac << 8) | adapter->Address[i]; | |||
| if (littleEndian) | |||
| mac = (int64) swapByteOrder ((uint64) mac); | |||
| if (numFound < maxNum && mac != 0) | |||
| addresses [numFound++] = mac; | |||
| @@ -262,7 +300,7 @@ static int getMACAddressViaGetAdaptersInfo (int64* addresses, int maxNum) throw( | |||
| return numFound; | |||
| } | |||
| static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||
| static int getMACAddressesViaNetBios (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| int numFound = 0; | |||
| @@ -315,6 +353,9 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||
| for (unsigned int i = 0; i < 6; ++i) | |||
| mac = (mac << 8) | astat.adapt.adapter_address[i]; | |||
| if (littleEndian) | |||
| mac = (int64) swapByteOrder ((uint64) mac); | |||
| if (numFound < maxNum && mac != 0) | |||
| addresses [numFound++] = mac; | |||
| } | |||
| @@ -326,14 +367,62 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum) throw() | |||
| return numFound; | |||
| } | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum) throw() | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| int numFound = getMACAddressViaGetAdaptersInfo (addresses, maxNum); | |||
| int numFound = getMACAddressViaGetAdaptersInfo (addresses, maxNum, littleEndian); | |||
| if (numFound == 0) | |||
| numFound = getMACAddressesViaNetBios (addresses, maxNum); | |||
| numFound = getMACAddressesViaNetBios (addresses, maxNum, littleEndian); | |||
| return numFound; | |||
| } | |||
| //============================================================================== | |||
| typedef ULONG (WINAPI *MAPISendMailType) (LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG); | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| HMODULE h = LoadLibraryA ("MAPI32.dll"); | |||
| MAPISendMailType mapiSendMail = (MAPISendMailType) GetProcAddress (h, "MAPISendMail"); | |||
| bool ok = false; | |||
| if (mapiSendMail != 0) | |||
| { | |||
| MapiMessage message; | |||
| zerostruct (message); | |||
| message.lpszSubject = (LPTSTR) (LPCTSTR) emailSubject; | |||
| message.lpszNoteText = (LPTSTR) (LPCTSTR) bodyText; | |||
| MapiRecipDesc recip; | |||
| zerostruct (recip); | |||
| recip.ulRecipClass = MAPI_TO; | |||
| recip.lpszName = (LPTSTR) (LPCTSTR) targetEmailAddress; | |||
| message.nRecipCount = 1; | |||
| message.lpRecips = &recip; | |||
| MemoryBlock mb (sizeof (MapiFileDesc) * filesToAttach.size()); | |||
| mb.fillWith (0); | |||
| MapiFileDesc* files = (MapiFileDesc*) mb.getData(); | |||
| message.nFileCount = filesToAttach.size(); | |||
| message.lpFiles = files; | |||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||
| { | |||
| files[i].nPosition = (ULONG) -1; | |||
| files[i].lpszPathName = (LPTSTR) (LPCTSTR) filesToAttach [i]; | |||
| } | |||
| ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG, 0) == SUCCESS_SUCCESS); | |||
| } | |||
| FreeLibrary (h); | |||
| return ok; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -141,7 +141,7 @@ | |||
| /** Enabling this builds support for VST audio plugins. | |||
| @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU | |||
| */ | |||
| #define JUCE_PLUGINHOST_VST 1 | |||
| //#define JUCE_PLUGINHOST_VST 1 | |||
| /** Enabling this builds support for AudioUnit audio plugins. | |||
| @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST | |||
| @@ -666,11 +666,16 @@ int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw | |||
| return viewport->getRowNumberOfComponent (rowComponent); | |||
| } | |||
| const Rectangle ListBox::getRowPosition (const int rowNumber) const throw() | |||
| const Rectangle ListBox::getRowPosition (const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const throw() | |||
| { | |||
| const int rowHeight = getRowHeight(); | |||
| int y = viewport->getY() + rowHeight * rowNumber; | |||
| return Rectangle (0, rowHeight * rowNumber, | |||
| if (relativeToComponentTopLeft) | |||
| y -= viewport->getViewPositionY(); | |||
| return Rectangle (viewport->getX(), y, | |||
| viewport->getViewedComponent()->getWidth(), rowHeight); | |||
| } | |||
| @@ -890,7 +895,7 @@ void ListBox::setHeaderComponent (Component* const newHeaderComponent) | |||
| void ListBox::repaintRow (const int rowNumber) throw() | |||
| { | |||
| const Rectangle r (getRowPosition (rowNumber)); | |||
| const Rectangle r (getRowPosition (rowNumber, true)); | |||
| repaint (r.getX(), r.getY(), r.getWidth(), r.getHeight()); | |||
| } | |||
| @@ -402,7 +402,8 @@ public: | |||
| This may be off-screen, and the range of the row number that is passed-in is | |||
| not checked to see if it's a valid row. | |||
| */ | |||
| const Rectangle getRowPosition (const int rowNumber) const throw(); | |||
| const Rectangle getRowPosition (const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const throw(); | |||
| /** Finds the row component for a given row in the list. | |||
| @@ -598,7 +598,7 @@ double Slider::getValueFromText (const String& text) | |||
| while (t.startsWithChar (T('+'))) | |||
| t = t.substring (1).trimStart(); | |||
| return t.initialSectionContainingOnly (T("0123456789.-")) | |||
| return t.initialSectionContainingOnly (T("0123456789.,-")) | |||
| .getDoubleValue(); | |||
| } | |||
| @@ -847,7 +847,7 @@ void Slider::resized() | |||
| } | |||
| } | |||
| const int indent = getLookAndFeel().getSliderThumbRadius (*this) + 2; | |||
| const int indent = getLookAndFeel().getSliderThumbRadius (*this); | |||
| if (style == LinearBar) | |||
| { | |||
| @@ -388,10 +388,16 @@ bool TableListBox::isAutoSizeMenuOptionShown() const throw() | |||
| return autoSizeOptionsShown; | |||
| } | |||
| const Rectangle TableListBox::getCellPosition (const int columnId, const int rowNumber) const | |||
| const Rectangle TableListBox::getCellPosition (const int columnId, | |||
| const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const | |||
| { | |||
| const Rectangle headerCell (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | |||
| const Rectangle row (getRowPosition (rowNumber)); | |||
| Rectangle headerCell (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | |||
| if (relativeToComponentTopLeft) | |||
| headerCell.translate (header->getX(), 0); | |||
| const Rectangle row (getRowPosition (rowNumber, relativeToComponentTopLeft)); | |||
| return Rectangle (headerCell.getX(), row.getY(), | |||
| headerCell.getWidth(), row.getHeight()); | |||
| @@ -406,7 +412,7 @@ void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | |||
| const Rectangle pos (header->getColumnPosition (header->getIndexOfColumnId (columnId, true))); | |||
| double x = scrollbar->getCurrentRangeStart(); | |||
| double w = scrollbar->getCurrentRangeSize(); | |||
| const double w = scrollbar->getCurrentRangeSize(); | |||
| if (pos.getX() < x) | |||
| x = pos.getX(); | |||
| @@ -264,11 +264,16 @@ public: | |||
| /** Returns the position of one of the cells in the table. | |||
| Co-ordinates are relative to the table component's top-left. The row number isn't | |||
| checked to see if it's in-range, but the column ID must exist or this will | |||
| return an empty rectangle. | |||
| If relativeToComponentTopLeft is true, the co-ordinates are relative to | |||
| the table component's top-left. The row number isn't checked to see if it's | |||
| in-range, but the column ID must exist or this will return an empty rectangle. | |||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | |||
| top-left of the table's top-left cell. | |||
| */ | |||
| const Rectangle getCellPosition (const int columnId, const int rowNumber) const; | |||
| const Rectangle getCellPosition (const int columnId, | |||
| const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const; | |||
| /** Scrolls horizontally if necessary to make sure that a particular column is visible. | |||
| @@ -57,7 +57,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "../filebrowser/juce_FilenameComponent.h" | |||
| #include "../filebrowser/juce_DirectoryContentsDisplayComponent.h" | |||
| #include "../filebrowser/juce_FileSearchPathListComponent.h" | |||
| #include "../filebrowser/juce_FileBrowserComponent.h" | |||
| #include "../filebrowser/juce_FileBrowserComponent.h" | |||
| #include "../layout/juce_GroupComponent.h" | |||
| #include "../properties/juce_PropertyComponent.h" | |||
| #include "../juce_Desktop.h" | |||
| @@ -814,6 +814,9 @@ void LookAndFeel::getIdealPopupMenuItemSize (const String& text, | |||
| { | |||
| Font font (getPopupMenuFont()); | |||
| if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f) | |||
| font.setHeight (standardMenuItemHeight / 1.3f); | |||
| idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundFloatToInt (font.getHeight() * 1.3f); | |||
| idealWidth = font.getStringWidth (text) + idealHeight * 2; | |||
| } | |||
| @@ -1137,7 +1140,7 @@ void LookAndFeel::drawLinearSliderBackground (Graphics& g, | |||
| const Slider::SliderStyle /*style*/, | |||
| Slider& slider) | |||
| { | |||
| const float sliderRadius = (float) getSliderThumbRadius (slider); | |||
| const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); | |||
| const Colour trackColour (slider.findColour (Slider::trackColourId)); | |||
| const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); | |||
| @@ -1186,7 +1189,7 @@ void LookAndFeel::drawLinearSliderThumb (Graphics& g, | |||
| const Slider::SliderStyle style, | |||
| Slider& slider) | |||
| { | |||
| const float sliderRadius = (float) getSliderThumbRadius (slider); | |||
| const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); | |||
| Colour knobColour (createBaseColour (slider.findColour (Slider::thumbColourId), | |||
| slider.hasKeyboardFocus (false) && slider.isEnabled(), | |||
| @@ -1277,7 +1280,7 @@ void LookAndFeel::drawLinearSlider (Graphics& g, | |||
| Colour baseColour (createBaseColour (slider.findColour (Slider::thumbColourId) | |||
| .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), | |||
| false, | |||
| isMouseOver, | |||
| isMouseOver, | |||
| isMouseOver || slider.isMouseButtonDown())); | |||
| drawShinyButtonShape (g, | |||
| @@ -1297,7 +1300,7 @@ int LookAndFeel::getSliderThumbRadius (Slider& slider) | |||
| { | |||
| return jmin (7, | |||
| slider.getHeight() / 2, | |||
| slider.getWidth() / 2); | |||
| slider.getWidth() / 2) + 2; | |||
| } | |||
| void LookAndFeel::drawRotarySlider (Graphics& g, | |||
| @@ -2358,7 +2361,7 @@ void LookAndFeel::drawFileBrowserRow (Graphics& g, int width, int height, | |||
| Button* LookAndFeel::createFileBrowserGoUpButton() | |||
| { | |||
| DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); | |||
| Path arrowPath; | |||
| arrowPath.addArrow (50.0f, 100.0f, 50.0f, 0.0, 40.0f, 100.0f, 50.0f); | |||
| @@ -40,6 +40,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "juce_Atomic.h" | |||
| #include "../threads/juce_Thread.h" | |||
| #include "../text/juce_LocalisedStrings.h" | |||
| void juce_initialiseStrings(); | |||
| //============================================================================== | |||
| @@ -78,6 +79,7 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() | |||
| juceInitialisedNonGUI = true; | |||
| DBG (SystemStats::getJUCEVersion()); | |||
| juce_initialiseStrings(); | |||
| SystemStats::initialiseStats(); | |||
| Random::getSystemRandom().setSeed (Time::currentTimeMillis()); | |||
| } | |||
| @@ -89,6 +91,10 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() | |||
| extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib; | |||
| #endif | |||
| #if JUCE_DEBUG | |||
| extern void juce_CheckForDanglingStreams(); | |||
| #endif | |||
| void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() | |||
| { | |||
| if (juceInitialisedNonGUI) | |||
| @@ -102,6 +108,10 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI() | |||
| LocalisedStrings::setCurrentMappings (0); | |||
| Thread::stopAllThreads (3000); | |||
| #if JUCE_DEBUG | |||
| juce_CheckForDanglingStreams(); | |||
| #endif | |||
| juceInitialisedNonGUI = false; | |||
| } | |||
| } | |||
| @@ -152,9 +152,21 @@ public: | |||
| @param addresses an array into which the MAC addresses should be copied | |||
| @param maxNum the number of elements in this array | |||
| @param littleEndian the endianness of the numbers to return. Note that | |||
| the default values of this parameter are different on | |||
| Mac/PC to avoid breaking old software that was written | |||
| before this parameter was added (when the two systems | |||
| defaulted to using different endiannesses). In newer | |||
| software you probably want to specify an explicit value | |||
| for this. | |||
| @returns the number of MAC addresses that were found | |||
| */ | |||
| static int getMACAddresses (int64* addresses, int maxNum) throw(); | |||
| static int getMACAddresses (int64* addresses, int maxNum, | |||
| #if JUCE_MAC | |||
| const bool littleEndian = true) throw(); | |||
| #else | |||
| const bool littleEndian = false) throw(); | |||
| #endif | |||
| //============================================================================== | |||
| @@ -35,6 +35,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "juce_URL.h" | |||
| #include "../../basics/juce_Random.h" | |||
| #include "../../text/juce_XmlDocument.h" | |||
| @@ -79,7 +80,9 @@ URL::URL (const String& url_) | |||
| URL::URL (const URL& other) | |||
| : url (other.url), | |||
| parameters (other.parameters) | |||
| parameters (other.parameters), | |||
| filesToUpload (other.filesToUpload), | |||
| mimeTypes (other.mimeTypes) | |||
| { | |||
| } | |||
| @@ -87,6 +90,8 @@ const URL& URL::operator= (const URL& other) | |||
| { | |||
| url = other.url; | |||
| parameters = other.parameters; | |||
| filesToUpload = other.filesToUpload; | |||
| mimeTypes = other.mimeTypes; | |||
| return *this; | |||
| } | |||
| @@ -95,7 +100,7 @@ URL::~URL() throw() | |||
| { | |||
| } | |||
| const String URL::getMangledParameters() const | |||
| static const String getMangledParameters (const StringPairArray& parameters) | |||
| { | |||
| String p; | |||
| @@ -104,9 +109,9 @@ const String URL::getMangledParameters() const | |||
| if (i > 0) | |||
| p += T("&"); | |||
| p << addEscapeChars (parameters.getAllKeys() [i]) | |||
| p << URL::addEscapeChars (parameters.getAllKeys() [i]) | |||
| << T("=") | |||
| << addEscapeChars (parameters.getAllValues() [i]); | |||
| << URL::addEscapeChars (parameters.getAllValues() [i]); | |||
| } | |||
| return p; | |||
| @@ -115,7 +120,7 @@ const String URL::getMangledParameters() const | |||
| const String URL::toString (const bool includeGetParameters) const | |||
| { | |||
| if (includeGetParameters && parameters.size() > 0) | |||
| return url + T("?") + getMangledParameters(); | |||
| return url + T("?") + getMangledParameters (parameters); | |||
| else | |||
| return url; | |||
| } | |||
| @@ -153,8 +158,12 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) | |||
| //============================================================================== | |||
| void* juce_openInternetFile (const String& url, | |||
| const String& optionalPostText, | |||
| const bool isPost); | |||
| const String& headers, | |||
| const MemoryBlock& optionalPostData, | |||
| const bool isPost, | |||
| URL::OpenStreamProgressCallback* callback, | |||
| void* callbackContext); | |||
| void juce_closeInternetFile (void* handle); | |||
| int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); | |||
| int juce_seekInInternetFile (void* handle, int newPosition); | |||
| @@ -164,23 +173,25 @@ int juce_getStatusCodeFor (void* handle); | |||
| //============================================================================== | |||
| class WebInputStream : public InputStream | |||
| { | |||
| String url, postText; | |||
| int64 position; | |||
| bool finished, isPost; | |||
| void* handle; | |||
| public: | |||
| //============================================================================== | |||
| WebInputStream (const String& url_, | |||
| const String& postText_, | |||
| const bool isPost_) | |||
| : url (url_), | |||
| postText (postText_), | |||
| position (0), | |||
| WebInputStream (const URL& url, | |||
| const bool isPost_, | |||
| URL::OpenStreamProgressCallback* const progressCallback_, | |||
| void* const progressCallbackContext_) | |||
| : position (0), | |||
| finished (false), | |||
| isPost (isPost_) | |||
| isPost (isPost_), | |||
| progressCallback (progressCallback_), | |||
| progressCallbackContext (progressCallbackContext_) | |||
| { | |||
| handle = juce_openInternetFile (url, postText, isPost); | |||
| server = url.toString (! isPost); | |||
| if (isPost_) | |||
| createHeadersAndPostData (url); | |||
| handle = juce_openInternetFile (server, headers, postData, isPost, | |||
| progressCallback_, progressCallbackContext_); | |||
| } | |||
| ~WebInputStream() | |||
| @@ -188,7 +199,7 @@ public: | |||
| juce_closeInternetFile (handle); | |||
| } | |||
| bool isError() const | |||
| bool isError() const throw() | |||
| { | |||
| return handle == 0; | |||
| } | |||
| @@ -253,7 +264,8 @@ public: | |||
| position = 0; | |||
| finished = false; | |||
| handle = juce_openInternetFile (url, postText, isPost); | |||
| handle = juce_openInternetFile (server, headers, postData, isPost, | |||
| progressCallback, progressCallbackContext); | |||
| } | |||
| skipNextBytes (wantedPos - position); | |||
| @@ -262,26 +274,105 @@ public: | |||
| return true; | |||
| } | |||
| }; | |||
| InputStream* URL::createInputStream (const bool usePostCommand) const | |||
| { | |||
| WebInputStream* wi | |||
| = (usePostCommand) ? new WebInputStream (url, getMangledParameters(), true) | |||
| : new WebInputStream (toString (true), String::empty, false); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| if (wi->isError()) | |||
| private: | |||
| String server, headers; | |||
| MemoryBlock postData; | |||
| int64 position; | |||
| bool finished; | |||
| const bool isPost; | |||
| void* handle; | |||
| URL::OpenStreamProgressCallback* const progressCallback; | |||
| void* const progressCallbackContext; | |||
| void createHeadersAndPostData (const URL& url) | |||
| { | |||
| delete wi; | |||
| wi = 0; | |||
| if (url.getFilesToUpload().size() > 0) | |||
| { | |||
| // need to upload some files, so do it as multi-part... | |||
| String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); | |||
| headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; | |||
| appendUTF8ToPostData ("--" + boundary); | |||
| int i; | |||
| for (i = 0; i < url.getParameters().size(); ++i) | |||
| { | |||
| String s; | |||
| s << "\r\nContent-Disposition: form-data; name=\"" | |||
| << url.getParameters().getAllKeys() [i] | |||
| << "\"\r\n\r\n" | |||
| << url.getParameters().getAllValues() [i] | |||
| << "\r\n--" | |||
| << boundary; | |||
| appendUTF8ToPostData (s); | |||
| } | |||
| for (i = 0; i < url.getFilesToUpload().size(); ++i) | |||
| { | |||
| const File f (url.getFilesToUpload().getAllValues() [i]); | |||
| const String paramName (url.getFilesToUpload().getAllKeys() [i]); | |||
| String s; | |||
| s << "\r\nContent-Disposition: form-data; name=\"" | |||
| << paramName | |||
| << "\"; filename=\"" | |||
| << f.getFileName() | |||
| << "\"\r\n"; | |||
| const String mimeType (url.getMimeTypesOfUploadFiles() | |||
| .getValue (paramName, String::empty)); | |||
| if (mimeType.isNotEmpty()) | |||
| s << "Content-Type: " << mimeType << "\r\n"; | |||
| s << "Content-Transfer-Encoding: binary\r\n\r\n"; | |||
| appendUTF8ToPostData (s); | |||
| f.loadFileAsData (postData); | |||
| s = "\r\n--" + boundary; | |||
| appendUTF8ToPostData (s); | |||
| } | |||
| appendUTF8ToPostData ("--\r\n"); | |||
| } | |||
| else | |||
| { | |||
| // just a short text attachment, so use simple url encoding.. | |||
| const String params (getMangledParameters (url.getParameters())); | |||
| headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " | |||
| + String ((int) strlen (params.toUTF8())) | |||
| + "\r\n"; | |||
| appendUTF8ToPostData (params); | |||
| } | |||
| } | |||
| return wi; | |||
| } | |||
| void appendUTF8ToPostData (const String& text) throw() | |||
| { | |||
| postData.append (text.toUTF8(), | |||
| (int) strlen (text.toUTF8())); | |||
| } | |||
| WebInputStream (const WebInputStream&); | |||
| const WebInputStream& operator= (const WebInputStream&); | |||
| }; | |||
| InputStream* URL::createPostInputStream (const String& postText) const | |||
| InputStream* URL::createInputStream (const bool usePostCommand, | |||
| OpenStreamProgressCallback* const progressCallback, | |||
| void* const progressCallbackContext) const | |||
| { | |||
| WebInputStream* wi = new WebInputStream (url, postText, true); | |||
| WebInputStream* wi = new WebInputStream (*this, usePostCommand, | |||
| progressCallback, progressCallbackContext); | |||
| if (wi->isError()) | |||
| { | |||
| @@ -338,11 +429,31 @@ const URL URL::withParameter (const String& parameterName, | |||
| return u; | |||
| } | |||
| const StringPairArray& URL::getParameters() const | |||
| const URL URL::withFileToUpload (const String& parameterName, | |||
| const File& fileToUpload, | |||
| const String& mimeType) const | |||
| { | |||
| URL u (*this); | |||
| u.filesToUpload.set (parameterName, fileToUpload.getFullPathName()); | |||
| u.mimeTypes.set (parameterName, mimeType); | |||
| return u; | |||
| } | |||
| const StringPairArray& URL::getParameters() const throw() | |||
| { | |||
| return parameters; | |||
| } | |||
| const StringPairArray& URL::getFilesToUpload() const throw() | |||
| { | |||
| return filesToUpload; | |||
| } | |||
| const StringPairArray& URL::getMimeTypesOfUploadFiles() const throw() | |||
| { | |||
| return mimeTypes; | |||
| } | |||
| //============================================================================== | |||
| const String URL::removeEscapeChars (const String& s) | |||
| { | |||
| @@ -87,6 +87,18 @@ public: | |||
| const URL withParameter (const String& parameterName, | |||
| const String& parameterValue) const; | |||
| /** Returns a copy of this URl, with a file-upload type parameter added to it. | |||
| When performing a POST where one of your parameters is a binary file, this | |||
| lets you specify the file. | |||
| Note that the filename is stored, but the file itself won't actually be read | |||
| until this URL is later used to create a network input stream. | |||
| */ | |||
| const URL withFileToUpload (const String& parameterName, | |||
| const File& fileToUpload, | |||
| const String& mimeType) const; | |||
| /** Returns a set of all the parameters encoded into the url. | |||
| E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would | |||
| @@ -96,7 +108,18 @@ public: | |||
| @see getNamedParameter, withParameter | |||
| */ | |||
| const StringPairArray& getParameters() const; | |||
| const StringPairArray& getParameters() const throw(); | |||
| /** Returns the set of files that should be uploaded as part of a POST operation. | |||
| This is the set of files that were added to the URL with the withFileToUpload() | |||
| method. | |||
| */ | |||
| const StringPairArray& getFilesToUpload() const throw(); | |||
| /** Returns the set of mime types associated with each of the upload files. | |||
| */ | |||
| const StringPairArray& getMimeTypesOfUploadFiles() const throw(); | |||
| //============================================================================== | |||
| /** Tries to launch the system's default browser to open the URL. | |||
| @@ -119,21 +142,28 @@ public: | |||
| static bool isProbablyAnEmailAddress (const String& possibleEmailAddress); | |||
| //============================================================================== | |||
| /** This callback function can be used by the createInputStream() method. | |||
| It allows your app to receive progress updates during a lengthy POST operation. If you | |||
| want to continue the operation, this should return true, or false to abort. | |||
| */ | |||
| typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes); | |||
| /** Attempts to open a stream that can read from this URL. | |||
| @param usePostCommand if true, it will try to do use a http 'POST' to pass | |||
| the paramters, otherwise it'll encode them into the | |||
| URL and do a 'GET'. | |||
| @param progressCallback if this is non-zero, it lets you supply a callback function | |||
| to keep track of the operation's progress. This can be useful | |||
| for lengthy POST operations, so that you can provide user feedback. | |||
| @param progressCallbackContext if a callback is specified, this value will be passed to | |||
| the function | |||
| */ | |||
| InputStream* createInputStream (const bool usePostCommand) const; | |||
| InputStream* createInputStream (const bool usePostCommand, | |||
| OpenStreamProgressCallback* const progressCallback = 0, | |||
| void* const progressCallbackContext = 0) const; | |||
| /** Attempts to open a stream to read from this URL using a http POST command. | |||
| Normally you'd use the createInputStream (true) method instead of this, as | |||
| this will pass the given block of text instead of any parameters | |||
| that were added to the this URL with the withParameter() method. | |||
| */ | |||
| InputStream* createPostInputStream (const String& postText) const; | |||
| //============================================================================== | |||
| /** Tries to download the entire contents of this URL into a binary data block. | |||
| @@ -206,9 +236,7 @@ public: | |||
| private: | |||
| String url; | |||
| StringPairArray parameters; | |||
| const String getMangledParameters() const; | |||
| StringPairArray parameters, filesToUpload, mimeTypes; | |||
| }; | |||
| @@ -47,6 +47,11 @@ public: | |||
| /** Plays the operating system's default alert 'beep' sound. */ | |||
| static void beep(); | |||
| static bool launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach); | |||
| #if JUCE_MAC || DOXYGEN | |||
| //============================================================================== | |||
| /** MAC ONLY - Turns a String into a pascal string. */ | |||