diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 9a644d3cb1..2558a2077d 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -537,7 +537,7 @@ #undef PACKED -#if JUCE_ASIO +#if JUCE_ASIO && JUCE_BUILD_NATIVE /* This is very frustrating - we only need to use a handful of definitions from a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy @@ -566,7 +566,7 @@ #include #endif -#if JUCE_USE_CDBURNER +#if JUCE_USE_CDBURNER && JUCE_BUILD_NATIVE /* You'll need the Platform SDK for these headers - if you don't have it and don't need to use CD-burning, then you might just want to disable the JUCE_USE_CDBURNER @@ -576,7 +576,7 @@ #include #endif -#if JUCE_USE_CAMERA +#if JUCE_USE_CAMERA && JUCE_BUILD_NATIVE /* If you're using the camera classes, you'll need access to a few DirectShow headers. @@ -599,7 +599,7 @@ #include #endif -#if JUCE_WASAPI +#if JUCE_WASAPI && JUCE_BUILD_NATIVE #include #include #include @@ -633,7 +633,7 @@ #pragma warning (pop) #endif -#if JUCE_DIRECT2D +#if JUCE_DIRECT2D && JUCE_BUILD_NATIVE #include #include #endif @@ -880,17 +880,21 @@ protected: #else #import #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #include + #if JUCE_BUILD_NATIVE + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #endif + #if JUCE_BUILD_MISC && (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) + #include + #endif #include #endif @@ -9257,6 +9261,62 @@ namespace URLHelpers return url[i] == ':' ? i + 1 : 0; } + + void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData) + { + MemoryOutputStream data (postData, false); + + if (url.getFilesToUpload().size() > 0) + { + // need to upload some files, so do it as multi-part... + const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); + + headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; + + data << "--" << boundary; + + int i; + for (i = 0; i < url.getParameters().size(); ++i) + { + data << "\r\nContent-Disposition: form-data; name=\"" + << url.getParameters().getAllKeys() [i] + << "\"\r\n\r\n" + << url.getParameters().getAllValues() [i] + << "\r\n--" + << boundary; + } + + for (i = 0; i < url.getFilesToUpload().size(); ++i) + { + const File file (url.getFilesToUpload().getAllValues() [i]); + const String paramName (url.getFilesToUpload().getAllKeys() [i]); + + data << "\r\nContent-Disposition: form-data; name=\"" << paramName + << "\"; filename=\"" << file.getFileName() << "\"\r\n"; + + const String mimeType (url.getMimeTypesOfUploadFiles() + .getValue (paramName, String::empty)); + + if (mimeType.isNotEmpty()) + data << "Content-Type: " << mimeType << "\r\n"; + + data << "Content-Transfer-Encoding: binary\r\n\r\n" + << file << "\r\n--" << boundary; + } + + data << "--\r\n"; + data.flush(); + } + else + { + data << getMangledParameters (url.getParameters()) << url.getPostData(); + data.flush(); + + // just a short text attachment, so use simple url encoding.. + headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " + << postData.getSize() << "\r\n"; + } + } } const String URL::toString (const bool includeGetParameters) const @@ -9331,27 +9391,20 @@ const URL URL::withNewSubPath (const String& newPath) const bool URL::isProbablyAWebsiteURL (const String& possibleURL) { - if (possibleURL.startsWithIgnoreCase ("http:") - || possibleURL.startsWithIgnoreCase ("ftp:")) - return true; + const char* validProtocols[] = { "http:", "ftp:", "https:" }; - if (possibleURL.startsWithIgnoreCase ("file:") - || possibleURL.containsChar ('@') - || possibleURL.endsWithChar ('.') - || (! possibleURL.containsChar ('.'))) - return false; - - if (possibleURL.startsWithIgnoreCase ("www.") - && possibleURL.substring (5).containsChar ('.')) - return true; - - const char* commonTLDs[] = { "com", "net", "org", "uk", "de", "fr", "jp" }; - - for (int i = 0; i < numElementsInArray (commonTLDs); ++i) - if ((possibleURL + "/").containsIgnoreCase ("." + String (commonTLDs[i]) + "/")) + for (int i = 0; i < numElementsInArray (validProtocols); ++i) + if (possibleURL.startsWithIgnoreCase (validProtocols[i])) return true; - return false; + if (possibleURL.containsChar ('@') + || possibleURL.containsChar (' ')) + return false; + + const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false) + .fromLastOccurrenceOf (".", false, false)); + + return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3; } bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) @@ -9363,190 +9416,6 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) && (! possibleEmailAddress.endsWithChar ('.')); } -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& optionalPostData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs); - -void juce_closeInternetFile (void* handle); -int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); -int juce_seekInInternetFile (void* handle, int newPosition); -int64 juce_getInternetFileContentLength (void* handle); -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers); - -class WebInputStream : public InputStream -{ -public: - - WebInputStream (const URL& url, - const bool isPost_, - URL::OpenStreamProgressCallback* const progressCallback_, - void* const progressCallbackContext_, - const String& extraHeaders, - const int timeOutMs_, - StringPairArray* const responseHeaders) - : position (0), - finished (false), - isPost (isPost_), - progressCallback (progressCallback_), - progressCallbackContext (progressCallbackContext_), - timeOutMs (timeOutMs_) - { - server = url.toString (! isPost); - - if (isPost_) - createHeadersAndPostData (url); - - headers += extraHeaders; - - if (! headers.endsWithChar ('\n')) - headers << "\r\n"; - - handle = juce_openInternetFile (server, headers, postData, isPost, - progressCallback_, progressCallbackContext_, - timeOutMs); - - if (responseHeaders != 0) - juce_getInternetFileHeaders (handle, *responseHeaders); - } - - ~WebInputStream() - { - juce_closeInternetFile (handle); - } - - bool isError() const { return handle == 0; } - int64 getTotalLength() { return juce_getInternetFileContentLength (handle); } - bool isExhausted() { return finished; } - int64 getPosition() { return position; } - - int read (void* dest, int bytes) - { - if (finished || isError()) - { - return 0; - } - else - { - const int bytesRead = juce_readFromInternetFile (handle, dest, bytes); - position += bytesRead; - - if (bytesRead == 0) - finished = true; - - return bytesRead; - } - } - - bool setPosition (int64 wantedPos) - { - if (wantedPos != position) - { - finished = false; - - const int actualPos = juce_seekInInternetFile (handle, (int) wantedPos); - - if (actualPos == wantedPos) - { - position = wantedPos; - } - else - { - if (wantedPos < position) - { - juce_closeInternetFile (handle); - - position = 0; - finished = false; - - handle = juce_openInternetFile (server, headers, postData, isPost, - progressCallback, progressCallbackContext, - timeOutMs); - } - - skipNextBytes (wantedPos - position); - } - } - - return true; - } - -private: - String server, headers; - MemoryBlock postData; - int64 position; - bool finished; - const bool isPost; - void* handle; - URL::OpenStreamProgressCallback* const progressCallback; - void* const progressCallbackContext; - const int timeOutMs; - - void createHeadersAndPostData (const URL& url) - { - MemoryOutputStream data (postData, false); - - if (url.getFilesToUpload().size() > 0) - { - // need to upload some files, so do it as multi-part... - const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); - - headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; - - data << "--" << boundary; - - int i; - for (i = 0; i < url.getParameters().size(); ++i) - { - data << "\r\nContent-Disposition: form-data; name=\"" - << url.getParameters().getAllKeys() [i] - << "\"\r\n\r\n" - << url.getParameters().getAllValues() [i] - << "\r\n--" - << boundary; - } - - for (i = 0; i < url.getFilesToUpload().size(); ++i) - { - const File file (url.getFilesToUpload().getAllValues() [i]); - const String paramName (url.getFilesToUpload().getAllKeys() [i]); - - data << "\r\nContent-Disposition: form-data; name=\"" << paramName - << "\"; filename=\"" << file.getFileName() << "\"\r\n"; - - const String mimeType (url.getMimeTypesOfUploadFiles() - .getValue (paramName, String::empty)); - - if (mimeType.isNotEmpty()) - data << "Content-Type: " << mimeType << "\r\n"; - - data << "Content-Transfer-Encoding: binary\r\n\r\n" - << file << "\r\n--" << boundary; - } - - data << "--\r\n"; - data.flush(); - } - else - { - data << URLHelpers::getMangledParameters (url.getParameters()) - << url.getPostData(); - - data.flush(); - - // just a short text attachment, so use simple url encoding.. - headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - + String ((unsigned int) postData.getSize()) - + "\r\n"; - } - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); -}; - InputStream* URL::createInputStream (const bool usePostCommand, OpenStreamProgressCallback* const progressCallback, void* const progressCallbackContext, @@ -9554,11 +9423,20 @@ InputStream* URL::createInputStream (const bool usePostCommand, const int timeOutMs, StringPairArray* const responseHeaders) const { - ScopedPointer wi (new WebInputStream (*this, usePostCommand, - progressCallback, progressCallbackContext, - extraHeaders, timeOutMs, responseHeaders)); + String headers; + MemoryBlock postData; - return wi->isError() ? 0 : wi.release(); + if (usePostCommand) + URLHelpers::createHeadersAndPostData (*this, headers, postData); + + headers += extraHeaders; + + if (! headers.endsWithChar ('\n')) + headers << "\r\n"; + + return createNativeStream (toString (! usePostCommand), usePostCommand, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders); } bool URL::readEntireBinaryStream (MemoryBlock& destData, @@ -24327,6 +24205,11 @@ double AudioTransportSource::getCurrentPosition() const return 0.0; } +double AudioTransportSource::getLengthInSeconds() const +{ + return getTotalLength() / sampleRate; +} + void AudioTransportSource::setNextReadPosition (int newPosition) { if (positionableSource != 0) @@ -36824,7 +36707,8 @@ private: AudioProcessorGraph& graph; const Array& orderedNodes; - Array nodeIds, channels, midiNodeIds; + Array channels; + Array nodeIds, midiNodeIds; void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, Array& renderingOps, @@ -37105,7 +36989,7 @@ private: return 0; } - int getBufferContaining (const int nodeId, const int outputChannel) const + int getBufferContaining (const uint32 nodeId, const int outputChannel) const { if (outputChannel == AudioProcessorGraph::midiChannelIndex) { @@ -37152,7 +37036,7 @@ private: bool isBufferNeededLater (int stepIndexToSearchFrom, int inputChannelOfIndexToIgnore, - const int nodeId, + const uint32 nodeId, const int outputChanIndex) const { while (stepIndexToSearchFrom < orderedNodes.size()) @@ -37182,7 +37066,7 @@ private: return false; } - void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) + void markBufferAsContaining (int bufferNum, uint32 nodeId, int outputIndex) { if (outputIndex == AudioProcessorGraph::midiChannelIndex) { @@ -56619,9 +56503,9 @@ void TreeView::clearSelectedItems() rootItem->deselectAllRecursively(); } -int TreeView::getNumSelectedItems() const throw() +int TreeView::getNumSelectedItems (int maximumDepthToSearchTo) const throw() { - return (rootItem != 0) ? rootItem->countSelectedItemsRecursively() : 0; + return (rootItem != 0) ? rootItem->countSelectedItemsRecursively (maximumDepthToSearchTo) : 0; } TreeViewItem* TreeView::getSelectedItem (const int index) const throw() @@ -57648,12 +57532,13 @@ TreeViewItem* TreeViewItem::findItemRecursively (int targetY) throw() return 0; } -int TreeViewItem::countSelectedItemsRecursively() const throw() +int TreeViewItem::countSelectedItemsRecursively (int depth) const throw() { int total = isSelected() ? 1 : 0; - for (int i = subItems.size(); --i >= 0;) - total += subItems.getUnchecked(i)->countSelectedItemsRecursively(); + if (depth != 0) + for (int i = subItems.size(); --i >= 0;) + total += subItems.getUnchecked(i)->countSelectedItemsRecursively (depth - 1); return total; } @@ -57679,7 +57564,7 @@ TreeViewItem* TreeViewItem::getSelectedItemWithIndex (int index) throw() if (found != 0) return found; - index -= item->countSelectedItemsRecursively(); + index -= item->countSelectedItemsRecursively (-1); } } @@ -78000,6 +77885,14 @@ DocumentWindow::DocumentWindow (const String& title, DocumentWindow::~DocumentWindow() { + // Don't delete or remove the resizer components yourself! They're managed by the + // DocumentWindow, and you should leave them alone! You may have deleted them + // accidentally by careless use of deleteAllChildren()..? + jassert (menuBar == 0 || getIndexOfChildComponent (menuBar) >= 0); + jassert (titleBarButtons[0] == 0 || getIndexOfChildComponent (titleBarButtons[0]) >= 0); + jassert (titleBarButtons[1] == 0 || getIndexOfChildComponent (titleBarButtons[1]) >= 0); + jassert (titleBarButtons[2] == 0 || getIndexOfChildComponent (titleBarButtons[2]) >= 0); + for (int i = numElementsInArray (titleBarButtons); --i >= 0;) titleBarButtons[i] = 0; @@ -237593,6 +237486,9 @@ private: #if JUCE_WINDOWS +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 + BEGIN_JUCE_NAMESPACE #define JUCE_INCLUDED_FILE 1 @@ -239280,242 +239176,278 @@ private: }; #endif -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) +class WebInputStream : public InputStream { - if (sessionHandle == 0) - sessionHandle = InternetOpen (_T("juce"), - INTERNET_OPEN_TYPE_PRECONFIG, - 0, 0, 0); +public: - if (sessionHandle != 0) + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : connection (0), request (0), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - // break up the url.. - TCHAR file[1024], server[1024]; + createConnection (progressCallback, progressCallbackContext); - URL_COMPONENTS uc; - zerostruct (uc); - - uc.dwStructSize = sizeof (uc); - uc.dwUrlPathLength = sizeof (file); - uc.dwHostNameLength = sizeof (server); - uc.lpszUrlPath = file; - uc.lpszHostName = server; - - if (InternetCrackUrl (url, 0, 0, &uc)) + if (responseHeaders != 0 && ! isError()) { - int disable = 1; - InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); - - if (timeOutMs == 0) - timeOutMs = 30000; - else if (timeOutMs < 0) - timeOutMs = -1; - - InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs)); - - const bool isFtp = url.startsWithIgnoreCase ("ftp:"); - -#if WORKAROUND_TIMEOUT_BUG - HINTERNET connection = 0; + DWORD bufferSizeBytes = 4096; + for (;;) { - InternetConnectThread connectThread (uc, connection, isFtp); - connectThread.wait (timeOutMs); + HeapBlock buffer ((size_t) bufferSizeBytes); - if (connection == 0) + if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { - InternetCloseHandle (sessionHandle); - sessionHandle = 0; - } - } -#else - HINTERNET connection = InternetConnect (sessionHandle, - uc.lpszHostName, - uc.nPort, - _T(""), _T(""), - isFtp ? INTERNET_SERVICE_FTP - : INTERNET_SERVICE_HTTP, - 0, 0); -#endif + StringArray headersArray; + headersArray.addLines (reinterpret_cast (buffer.getData())); - if (connection != 0) - { - if (isFtp) - { - HINTERNET request = FtpOpenFile (connection, - uc.lpszUrlPath, - GENERIC_READ, - FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, - 0); - - ConnectionAndRequestStruct* const result = new ConnectionAndRequestStruct(); - result->connection = connection; - result->request = request; - return result; - } - else - { - const TCHAR* mimeTypes[] = { _T("*/*"), 0 }; - - DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; - - if (url.startsWithIgnoreCase ("https:")) - flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - - // IE7 seems to automatically work out when it's https) - - HINTERNET request = HttpOpenRequest (connection, - isPost ? _T("POST") - : _T("GET"), - uc.lpszUrlPath, - 0, 0, mimeTypes, flags, 0); - - if (request != 0) + for (int i = 0; i < headersArray.size(); ++i) { - INTERNET_BUFFERS buffers; - zerostruct (buffers); - buffers.dwStructSize = sizeof (INTERNET_BUFFERS); - buffers.lpcszHeader = (LPCTSTR) headers; - buffers.dwHeadersLength = headers.length(); - buffers.dwBufferTotal = (DWORD) postData.getSize(); - ConnectionAndRequestStruct* result = 0; + const String& header = headersArray[i]; + const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String value (header.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); - if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + + break; + } + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; + } + + } + } + + ~WebInputStream() + { + close(); + } + + bool isError() const { return request == 0; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } + + int64 getTotalLength() + { + if (! isError()) + { + DWORD index = 0, result = 0, size = sizeof (result); + + if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) + return (int64) result; + } + + return -1; + } + + int read (void* buffer, int bytesToRead) + { + DWORD bytesRead = 0; + + if (! (finished || isError())) + { + InternetReadFile (request, buffer, bytesToRead, &bytesRead); + position += bytesRead; + + if (bytesRead == 0) + finished = true; + } + + return (int) bytesRead; + } + + bool setPosition (int64 wantedPos) + { + if (isError()) + return false; + + if (wantedPos != position) + { + finished = false; + position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0); + + if (position == wantedPos) + return true; + + if (wantedPos < position) + { + close(); + position = 0; + createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + +private: + + HINTERNET connection, request; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + int timeOutMs; + + void close() + { + if (request != 0) + { + InternetCloseHandle (request); + request = 0; + } + + if (connection != 0) + { + InternetCloseHandle (connection); + connection = 0; + } + } + + void createConnection (URL::OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext) + { + static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); + + close(); + + if (sessionHandle != 0) + { + // break up the url.. + TCHAR file[1024], server[1024]; + + URL_COMPONENTS uc; + zerostruct (uc); + uc.dwStructSize = sizeof (uc); + uc.dwUrlPathLength = sizeof (file); + uc.dwHostNameLength = sizeof (server); + uc.lpszUrlPath = file; + uc.lpszHostName = server; + + if (InternetCrackUrl (address, 0, 0, &uc)) + { + int disable = 1; + InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); + + if (timeOutMs == 0) + timeOutMs = 30000; + else if (timeOutMs < 0) + timeOutMs = -1; + + InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs)); + + const bool isFtp = address.startsWithIgnoreCase ("ftp:"); + + #if WORKAROUND_TIMEOUT_BUG + connection = 0; + + { + InternetConnectThread connectThread (uc, connection, isFtp); + connectThread.wait (timeOutMs); + + if (connection == 0) + { + InternetCloseHandle (sessionHandle); + sessionHandle = 0; + } + } + #else + connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, + _T(""), _T(""), + isFtp ? INTERNET_SERVICE_FTP + : INTERNET_SERVICE_HTTP, + 0, 0); + #endif + + if (connection != 0) + { + if (isFtp) + { + request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ, + FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0); + } + else + { + const TCHAR* mimeTypes[] = { _T("*/*"), 0 }; + + DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; + + if (address.startsWithIgnoreCase ("https:")) + flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - + // IE7 seems to automatically work out when it's https) + + request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"), + uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0); + + if (request != 0) { - int bytesSent = 0; + INTERNET_BUFFERS buffers; + zerostruct (buffers); + buffers.dwStructSize = sizeof (INTERNET_BUFFERS); + buffers.lpcszHeader = static_cast (headers); + buffers.dwHeadersLength = headers.length(); + buffers.dwBufferTotal = (DWORD) postData.getSize(); + ConnectionAndRequestStruct* result = 0; - for (;;) + if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) { - const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); - DWORD bytesDone = 0; + int bytesSent = 0; - if (bytesToDo > 0 - && ! InternetWriteFile (request, - static_cast (postData.getData()) + bytesSent, - bytesToDo, &bytesDone)) + for (;;) { - break; - } + const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); + DWORD bytesDone = 0; - if (bytesToDo == 0 || (int) bytesDone < bytesToDo) - { - result = new ConnectionAndRequestStruct(); - result->connection = connection; - result->request = request; - - if (! HttpEndRequest (request, 0, 0, 0)) + if (bytesToDo > 0 + && ! InternetWriteFile (request, + static_cast (postData.getData()) + bytesSent, + bytesToDo, &bytesDone)) + { break; + } - return result; + if (bytesToDo == 0 || (int) bytesDone < bytesToDo) + { + if (HttpEndRequest (request, 0, 0, 0)) + return; + + break; + } + + bytesSent += bytesDone; + + if (progressCallback != 0 && ! progressCallback (progressCallbackContext, bytesSent, postData.getSize())) + break; } - - bytesSent += bytesDone; - - if (callback != 0 && ! callback (callbackContext, bytesSent, postData.getSize())) - break; } } - InternetCloseHandle (request); + close(); } - - InternetCloseHandle (connection); } } } } - return 0; -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); +}; -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - DWORD bytesRead = 0; - const ConnectionAndRequestStruct* const crs = static_cast (handle); + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (crs != 0) - InternetReadFile (crs->request, - buffer, bytesToRead, - &bytesRead); - - return bytesRead; -} - -int juce_seekInInternetFile (void* handle, int newPosition) -{ - if (handle != 0) - { - const ConnectionAndRequestStruct* const crs = static_cast (handle); - return InternetSetFilePointer (crs->request, newPosition, 0, FILE_BEGIN, 0); - } - - return -1; -} - -int64 juce_getInternetFileContentLength (void* handle) -{ - const ConnectionAndRequestStruct* const crs = static_cast (handle); - - if (crs != 0) - { - DWORD index = 0, result = 0, size = sizeof (result); - - if (HttpQueryInfo (crs->request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) - return (int64) result; - } - - return -1; -} - -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - const ConnectionAndRequestStruct* const crs = static_cast (handle); - - if (crs != 0) - { - DWORD bufferSizeBytes = 4096; - - for (;;) - { - HeapBlock buffer ((size_t) bufferSizeBytes); - - if (HttpQueryInfo (crs->request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) - { - StringArray headersArray; - headersArray.addLines (reinterpret_cast (buffer.getData())); - - for (int i = 0; i < headersArray.size(); ++i) - { - const String& header = headersArray[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); - const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (headers [key]); - - headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); - } - - break; - } - - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; - } - } -} - -void juce_closeInternetFile (void* handle) -{ - if (handle != 0) - { - ScopedPointer crs (static_cast (handle)); - InternetCloseHandle (crs->request); - InternetCloseHandle (crs->connection); - } + return wi->isError() ? 0 : wi.release(); } namespace MACAddressHelpers @@ -243281,8 +243213,7 @@ private: { const Rectangle r (component->getParentMonitorArea()); - SetWindowPos (hwnd, 0, - r.getX(), r.getY(), r.getWidth(), r.getHeight(), + SetWindowPos (hwnd, 0, r.getX(), r.getY(), r.getWidth(), r.getHeight(), SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING); } } @@ -243293,7 +243224,10 @@ public: Win32ComponentPeer* const peer = getOwnerOfWindow (h); if (peer != 0) + { + jassert (isValidPeer (peer)); return peer->peerWindowProc (h, message, wParam, lParam); + } return DefWindowProcW (h, message, wParam, lParam); } @@ -243324,320 +243258,313 @@ private: LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) { - if (isValidPeer (this)) + switch (message) { - switch (message) - { - case WM_NCHITTEST: - if ((styleFlags & windowIgnoresMouseClicks) != 0) - return HTTRANSPARENT; - else if (! hasTitleBar()) - return HTCLIENT; + case WM_NCHITTEST: + if ((styleFlags & windowIgnoresMouseClicks) != 0) + return HTTRANSPARENT; + else if (! hasTitleBar()) + return HTCLIENT; - break; + break; - case WM_PAINT: + case WM_PAINT: + handlePaintMessage(); + return 0; + + case WM_NCPAINT: + if (wParam != 1) handlePaintMessage(); - return 0; - - case WM_NCPAINT: - if (wParam != 1) - handlePaintMessage(); - - if (hasTitleBar()) - break; - - return 0; - - case WM_ERASEBKGND: - case WM_NCCALCSIZE: - if (hasTitleBar()) - break; - - return 1; - - case WM_MOUSEMOVE: - doMouseMove (getPointFromLParam (lParam)); - return 0; - - case WM_MOUSELEAVE: - doMouseExit(); - return 0; - - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - doMouseDown (getPointFromLParam (lParam), wParam); - return 0; - - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - doMouseUp (getPointFromLParam (lParam), wParam); - return 0; - - case WM_CAPTURECHANGED: - doCaptureChanged(); - return 0; - - case WM_NCMOUSEMOVE: - if (hasTitleBar()) - break; - - return 0; - - case 0x020A: /* WM_MOUSEWHEEL */ - doMouseWheel (getCurrentMousePos(), wParam, true); - return 0; - - case 0x020E: /* WM_MOUSEHWHEEL */ - doMouseWheel (getCurrentMousePos(), wParam, false); - return 0; - - case WM_SIZING: - return handleSizeConstraining ((RECT*) lParam, wParam); - - case WM_WINDOWPOSCHANGING: - return handlePositionChanging ((WINDOWPOS*) lParam); - - case WM_WINDOWPOSCHANGED: - { - const Point pos (getCurrentMousePos()); - if (contains (pos, false)) - doMouseEvent (pos); - } - - handleMovedOrResized(); - - if (dontRepaint) - break; // needed for non-accelerated openGL windows to draw themselves correctly.. - - return 0; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (doKeyDown (wParam)) - return 0; + if (hasTitleBar()) break; - case WM_KEYUP: - case WM_SYSKEYUP: - if (doKeyUp (wParam)) - return 0; + return 0; + case WM_ERASEBKGND: + case WM_NCCALCSIZE: + if (hasTitleBar()) break; - case WM_CHAR: - if (doKeyChar ((int) wParam, lParam)) - return 0; + return 1; + case WM_MOUSEMOVE: + doMouseMove (getPointFromLParam (lParam)); + return 0; + + case WM_MOUSELEAVE: + doMouseExit(); + return 0; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + doMouseDown (getPointFromLParam (lParam), wParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + doMouseUp (getPointFromLParam (lParam), wParam); + return 0; + + case WM_CAPTURECHANGED: + doCaptureChanged(); + return 0; + + case WM_NCMOUSEMOVE: + if (hasTitleBar()) break; - case WM_APPCOMMAND: - if (doAppCommand (lParam)) - return TRUE; + return 0; - break; + case 0x020A: /* WM_MOUSEWHEEL */ + case 0x020E: /* WM_MOUSEHWHEEL */ + doMouseWheel (getCurrentMousePos(), wParam, message == 0x020A); + return 0; - case WM_SETFOCUS: - updateKeyModifiers(); - handleFocusGain(); - break; + case WM_SIZING: + return handleSizeConstraining ((RECT*) lParam, wParam); - case WM_KILLFOCUS: - if (hasCreatedCaret) - { - hasCreatedCaret = false; - DestroyCaret(); - } + case WM_WINDOWPOSCHANGING: + return handlePositionChanging ((WINDOWPOS*) lParam); - handleFocusLoss(); - break; + case WM_WINDOWPOSCHANGED: + { + const Point pos (getCurrentMousePos()); + if (contains (pos, false)) + doMouseEvent (pos); + } - case WM_ACTIVATEAPP: - // Windows does weird things to process priority when you swap apps, - // so this forces an update when the app is brought to the front - if (wParam != FALSE) - juce_repeatLastProcessPriority(); - else - Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus + handleMovedOrResized(); - juce_CheckCurrentlyFocusedTopLevelWindow(); - modifiersAtLastCallback = -1; + if (dontRepaint) + break; // needed for non-accelerated openGL windows to draw themselves correctly.. + + return 0; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (doKeyDown (wParam)) return 0; - case WM_ACTIVATE: - if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE) - { - handleAppActivation (wParam); - return 0; - } - - break; - - case WM_NCACTIVATE: - // while a temporary window is being shown, prevent Windows from deactivating the - // title bars of our main windows. - if (wParam == 0 && ! shouldDeactivateTitleBar) - wParam = TRUE; // change this and let it get passed to the DefWindowProc. - - break; - - case WM_MOUSEACTIVATE: - if (! component->getMouseClickGrabsKeyboardFocus()) - return MA_NOACTIVATE; - - break; - - case WM_SHOWWINDOW: - if (wParam != 0) - handleBroughtToFront(); - - break; - - case WM_CLOSE: - if (! component->isCurrentlyBlockedByAnotherModalComponent()) - handleUserClosingWindow(); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + if (doKeyUp (wParam)) return 0; - case WM_QUERYENDSESSION: - if (JUCEApplication::getInstance() != 0) - { - JUCEApplication::getInstance()->systemRequestedQuit(); - return MessageManager::getInstance()->hasStopMessageBeenSent(); - } + break; + + case WM_CHAR: + if (doKeyChar ((int) wParam, lParam)) + return 0; + + break; + + case WM_APPCOMMAND: + if (doAppCommand (lParam)) return TRUE; - case WM_TRAYNOTIFY: - handleTaskBarEvent (lParam, wParam); + break; + + case WM_SETFOCUS: + updateKeyModifiers(); + handleFocusGain(); + break; + + case WM_KILLFOCUS: + if (hasCreatedCaret) + { + hasCreatedCaret = false; + DestroyCaret(); + } + + handleFocusLoss(); + break; + + case WM_ACTIVATEAPP: + // Windows does weird things to process priority when you swap apps, + // so this forces an update when the app is brought to the front + if (wParam != FALSE) + juce_repeatLastProcessPriority(); + else + Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus + + juce_CheckCurrentlyFocusedTopLevelWindow(); + modifiersAtLastCallback = -1; + return 0; + + case WM_ACTIVATE: + if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE) + { + handleAppActivation (wParam); + return 0; + } + + break; + + case WM_NCACTIVATE: + // while a temporary window is being shown, prevent Windows from deactivating the + // title bars of our main windows. + if (wParam == 0 && ! shouldDeactivateTitleBar) + wParam = TRUE; // change this and let it get passed to the DefWindowProc. + + break; + + case WM_MOUSEACTIVATE: + if (! component->getMouseClickGrabsKeyboardFocus()) + return MA_NOACTIVATE; + + break; + + case WM_SHOWWINDOW: + if (wParam != 0) + handleBroughtToFront(); + + break; + + case WM_CLOSE: + if (! component->isCurrentlyBlockedByAnotherModalComponent()) + handleUserClosingWindow(); + + return 0; + + case WM_QUERYENDSESSION: + if (JUCEApplication::getInstance() != 0) + { + JUCEApplication::getInstance()->systemRequestedQuit(); + return MessageManager::getInstance()->hasStopMessageBeenSent(); + } + return TRUE; + + case WM_TRAYNOTIFY: + handleTaskBarEvent (lParam, wParam); + break; + + case WM_SYNCPAINT: + return 0; + + case WM_PALETTECHANGED: + InvalidateRect (h, 0, 0); + break; + + case WM_DISPLAYCHANGE: + InvalidateRect (h, 0, 0); + createPaletteIfNeeded = true; + // intentional fall-through... + case WM_SETTINGCHANGE: // note the fall-through in the previous case! + doSettingChange(); + break; + + case WM_INITMENU: + if (! hasTitleBar()) + { + if (isFullScreen()) + { + EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); + } + else if (! isMinimised()) + { + EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); + } + } + break; + + case WM_SYSCOMMAND: + switch (wParam & 0xfff0) + { + case SC_CLOSE: + if (sendInputAttemptWhenModalMessage()) + return 0; + + if (hasTitleBar()) + { + PostMessage (h, WM_CLOSE, 0, 0); + return 0; + } break; - case WM_SYNCPAINT: + case SC_KEYMENU: + // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure + // situations that can arise if a modal loop is started from an alt-key keypress). + if (hasTitleBar() && h == GetCapture()) + ReleaseCapture(); + + break; + + case SC_MAXIMIZE: + if (! sendInputAttemptWhenModalMessage()) + return 0; + + setFullScreen (true); return 0; - case WM_PALETTECHANGED: - InvalidateRect (h, 0, 0); - break; + case SC_MINIMIZE: + if (sendInputAttemptWhenModalMessage()) + return 0; - case WM_DISPLAYCHANGE: - InvalidateRect (h, 0, 0); - createPaletteIfNeeded = true; - // intentional fall-through... - case WM_SETTINGCHANGE: // note the fall-through in the previous case! - doSettingChange(); - break; - - case WM_INITMENU: if (! hasTitleBar()) + { + setMinimised (true); + return 0; + } + break; + + case SC_RESTORE: + if (sendInputAttemptWhenModalMessage()) + return 0; + + if (hasTitleBar()) { if (isFullScreen()) { - EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); - } - else if (! isMinimised()) - { - EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); + setFullScreen (false); + return 0; } } - break; - - case WM_SYSCOMMAND: - switch (wParam & 0xfff0) + else { - case SC_CLOSE: - if (sendInputAttemptWhenModalMessage()) - return 0; + if (isMinimised()) + setMinimised (false); + else if (isFullScreen()) + setFullScreen (false); - if (hasTitleBar()) - { - PostMessage (h, WM_CLOSE, 0, 0); - return 0; - } - break; - - case SC_KEYMENU: - // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure - // situations that can arise if a modal loop is started from an alt-key keypress). - if (hasTitleBar() && h == GetCapture()) - ReleaseCapture(); - - break; - - case SC_MAXIMIZE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - setFullScreen (true); return 0; - - case SC_MINIMIZE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - if (! hasTitleBar()) - { - setMinimised (true); - return 0; - } - break; - - case SC_RESTORE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - if (hasTitleBar()) - { - if (isFullScreen()) - { - setFullScreen (false); - return 0; - } - } - else - { - if (isMinimised()) - setMinimised (false); - else if (isFullScreen()) - setFullScreen (false); - - return 0; - } - - break; } - break; + } - case WM_NCLBUTTONDOWN: - case WM_NCRBUTTONDOWN: - case WM_NCMBUTTONDOWN: - sendInputAttemptWhenModalMessage(); - break; + break; - //case WM_IME_STARTCOMPOSITION; - // return 0; + case WM_NCLBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN: + sendInputAttemptWhenModalMessage(); + break; - case WM_GETDLGCODE: - return DLGC_WANTALLKEYS; + //case WM_IME_STARTCOMPOSITION; + // return 0; - default: - if (taskBarIcon != 0) + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + if (taskBarIcon != 0) + { + static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated")); + + if (message == taskbarCreatedMessage) { - static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated")); - - if (message == taskbarCreatedMessage) - { - taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; - Shell_NotifyIcon (NIM_ADD, taskBarIcon); - } + taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + Shell_NotifyIcon (NIM_ADD, taskBarIcon); } + } - break; - } + break; } return DefWindowProcW (h, message, wParam, lParam); @@ -253630,6 +253557,9 @@ END_JUCE_NAMESPACE #if JUCE_LINUX +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 + BEGIN_JUCE_NAMESPACE #define JUCE_INCLUDED_FILE 1 @@ -254921,32 +254851,112 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd return false; } -/** A HTTP input stream that uses sockets. - */ -class JUCE_HTTPSocketStream +class WebInputStream : public InputStream { public: - JUCE_HTTPSocketStream() - : readPosition (0), - socketHandle (-1), - levelsOfRedirection (0), - timeoutSeconds (15) + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : socketHandle (-1), levelsOfRedirection (0), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { + createConnection (progressCallback, progressCallbackContext); + + if (responseHeaders != 0 && ! isError()) + { + for (int i = 0; i < headerLines.size(); ++i) + { + const String& headersEntry = headerLines[i]; + const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); + const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + } } - ~JUCE_HTTPSocketStream() + ~WebInputStream() { closeSocket(); } - bool open (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) + bool isError() const { return socketHandle < 0; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } + + int64 getTotalLength() + { + jassertfalse; //xxx to do + return -1; + } + + int read (void* buffer, int bytesToRead) + { + if (finished || isError()) + return 0; + + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = jmax (1, timeOutMs / 1000); + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return 0; // (timeout) + + const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + if (bytesRead == 0) + finished = true; + position += bytesRead; + return bytesRead; + } + + bool setPosition (int64 wantedPos) + { + if (isError()) + return false; + + if (wantedPos != position) + { + finished = false; + + if (wantedPos < position) + { + closeSocket(); + position = 0; + createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + +private: + int socketHandle, levelsOfRedirection; + StringArray headerLines; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + const int timeOutMs; + + void closeSocket() + { + if (socketHandle >= 0) + close (socketHandle); + + socketHandle = -1; + levelsOfRedirection = 0; + } + + void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { closeSocket(); @@ -254961,9 +254971,8 @@ public: String hostName, hostPath; int hostPort; - - if (! decomposeURL (url, hostName, hostPath, hostPort)) - return false; + if (! decomposeURL (address, hostName, hostPath, hostPort)) + return; const struct hostent* host = 0; int port = 0; @@ -254975,7 +254984,7 @@ public: if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) - return false; + return; host = gethostbyname (proxyName.toUTF8()); port = proxyPort; @@ -254987,64 +254996,47 @@ public: } if (host == 0) - return false; + return; - struct sockaddr_in address; - zerostruct (address); - memcpy (&address.sin_addr, host->h_addr, host->h_length); - address.sin_family = host->h_addrtype; - address.sin_port = htons (port); - - socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); - - if (socketHandle == -1) - return false; - - int receiveBufferSize = 16384; - setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); - setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); - -#if JUCE_MAC - setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); -#endif - - if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) { - closeSocket(); - return false; - } + struct sockaddr_in socketAddress; + zerostruct (socketAddress); + memcpy (&socketAddress.sin_addr, host->h_addr, host->h_length); + socketAddress.sin_family = host->h_addrtype; + socketAddress.sin_port = htons (port); - const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, - hostPath, url, headers, postData, isPost)); - size_t totalHeaderSent = 0; + socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); - while (totalHeaderSent < requestHeader.getSize()) - { - if (Time::getMillisecondCounter() > timeOutTime) + if (socketHandle == -1) + return; + + int receiveBufferSize = 16384; + setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + + #if JUCE_MAC + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); + #endif + + if (connect (socketHandle, (struct sockaddr*) &socketAddress, sizeof (socketAddress)) == -1) { closeSocket(); - return false; - } - - const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); - - if (send (socketHandle, ((const char*) requestHeader.getData()) + totalHeaderSent, numToSend, 0) - != numToSend) - { - closeSocket(); - return false; - } - - totalHeaderSent += numToSend; - - if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) - { - closeSocket(); - return false; + return; } } - const String responseHeader (readResponse (timeOutTime)); + { + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, + hostPath, address, headers, postData, isPost)); + + if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) + { + closeSocket(); + return; + } + } + + const String responseHeader (readResponse (socketHandle, timeOutTime)); if (responseHeader.isNotEmpty()) { @@ -255059,109 +255051,42 @@ public: String location (findHeaderItem (headerLines, "Location:")); - if (statusCode >= 300 && statusCode < 400 - && location.isNotEmpty()) + if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) { if (! location.startsWithIgnoreCase ("http://")) location = "http://" + location; - if (levelsOfRedirection++ < 3) - return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); + if (++levelsOfRedirection <= 3) + { + address = location; + createConnection (progressCallback, progressCallbackContext); + return; + } } else { levelsOfRedirection = 0; - return true; + return; } } closeSocket(); - return false; } - int read (void* buffer, int bytesToRead) - { - fd_set readbits; - FD_ZERO (&readbits); - FD_SET (socketHandle, &readbits); - - struct timeval tv; - tv.tv_sec = timeoutSeconds; - tv.tv_usec = 0; - - if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) - return 0; // (timeout) - - const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); - readPosition += bytesRead; - return bytesRead; - } - - int readPosition; - StringArray headerLines; - -private: - int socketHandle, levelsOfRedirection; - const int timeoutSeconds; - - void closeSocket() - { - if (socketHandle >= 0) - close (socketHandle); - - socketHandle = -1; - } - - const MemoryBlock createRequestHeader (const String& hostName, - const int hostPort, - const String& proxyName, - const int proxyPort, - const String& hostPath, - const String& originalURL, - const String& headers, - const MemoryBlock& postData, - const bool isPost) - { - String header (isPost ? "POST " : "GET "); - - if (proxyName.isEmpty()) - { - header << hostPath << " HTTP/1.0\r\nHost: " - << hostName << ':' << hostPort; - } - else - { - header << originalURL << " HTTP/1.0\r\nHost: " - << proxyName << ':' << proxyPort; - } - - header << "\r\nUser-Agent: JUCE/" - << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION - << "\r\nConnection: Close\r\nContent-Length: " - << postData.getSize() << "\r\n" - << headers << "\r\n"; - - MemoryBlock mb; - mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); - mb.append (postData.getData(), postData.getSize()); - - return mb; - } - - const String readResponse (const uint32 timeOutTime) + static const String readResponse (const int socketHandle, const uint32 timeOutTime) { int bytesRead = 0, numConsecutiveLFs = 0; MemoryBlock buffer (1024, true); while (numConsecutiveLFs < 2 && bytesRead < 32768 - && Time::getMillisecondCounter() <= timeOutTime) + && Time::getMillisecondCounter() <= timeOutTime) { fd_set readbits; FD_ZERO (&readbits); FD_SET (socketHandle, &readbits); struct timeval tv; - tv.tv_sec = timeoutSeconds; + tv.tv_sec = jmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); tv.tv_usec = 0; if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) @@ -255190,8 +255115,62 @@ private: return String::empty; } - static bool decomposeURL (const String& url, - String& host, String& path, int& port) + static const MemoryBlock createRequestHeader (const String& hostName, const int hostPort, + const String& proxyName, const int proxyPort, + const String& hostPath, const String& originalURL, + const String& headers, const MemoryBlock& postData, + const bool isPost) + { + String header (isPost ? "POST " : "GET "); + + if (proxyName.isEmpty()) + { + header << hostPath << " HTTP/1.0\r\nHost: " + << hostName << ':' << hostPort; + } + else + { + header << originalURL << " HTTP/1.0\r\nHost: " + << proxyName << ':' << proxyPort; + } + + header << "\r\nUser-Agent: JUCE/" << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION + << "\r\nConnection: Close\r\nContent-Length: " + << postData.getSize() << "\r\n" + << headers << "\r\n"; + + MemoryBlock mb; + mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); + mb.append (postData.getData(), postData.getSize()); + + return mb; + } + + static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + { + size_t totalHeaderSent = 0; + + while (totalHeaderSent < requestHeader.getSize()) + { + if (Time::getMillisecondCounter() > timeOutTime) + return false; + + const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); + + if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) + return false; + + totalHeaderSent += numToSend; + + if (progressCallback != 0 && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) + return false; + } + + return true; + } + + static bool decomposeURL (const String& url, String& host, String& path, int& port) { if (! url.startsWithIgnoreCase ("http://")) return false; @@ -255237,72 +255216,18 @@ private: return String::empty; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JUCE_HTTPSocketStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); }; -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - ScopedPointer s (new JUCE_HTTPSocketStream()); + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) - return s.release(); - - return 0; -} - -void juce_closeInternetFile (void* handle) -{ - delete static_cast (handle); -} - -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - return s != 0 ? s->read (buffer, bytesToRead) : 0; -} - -int64 juce_getInternetFileContentLength (void* handle) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - if (s != 0) - { - //xxx todo - jassertfalse - } - - return -1; -} - -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - if (s != 0) - { - for (int i = 0; i < s->headerLines.size(); ++i) - { - const String& headersEntry = s->headerLines[i]; - const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); - const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (headers [key]); - headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); - } - } -} - -int juce_seekInInternetFile (void* handle, int newPosition) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - return s != 0 ? s->readPosition : 0; + return wi->isError() ? 0 : wi.release(); } #endif @@ -262345,6 +262270,9 @@ END_JUCE_NAMESPACE #if JUCE_MAC || JUCE_IOS +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 + BEGIN_JUCE_NAMESPACE #undef Point @@ -262873,8 +262801,6 @@ using namespace JUCE_NAMESPACE; class JuceURLConnectionMessageThread : public Thread { - JuceURLConnection* owner; - public: JuceURLConnectionMessageThread (JuceURLConnection* owner_) : Thread ("http connection"), @@ -262897,6 +262823,9 @@ public: [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } } + +private: + JuceURLConnection* owner; }; @implementation JuceURLConnection @@ -263054,117 +262983,151 @@ public: } @end + BEGIN_JUCE_NAMESPACE -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) +class WebInputStream : public InputStream { - const ScopedAutoReleasePool pool; +public: - 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) + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : connection (nil), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); - const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); + JUCE_AUTORELEASEPOOL + connection = createConnection (progressCallback, progressCallbackContext); - if (key.isNotEmpty() && value.isNotEmpty()) - [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; + if (responseHeaders != 0 && connection != 0 && connection->headers != 0) + { + NSEnumerator* enumerator = [connection->headers keyEnumerator]; + NSString* key; + + while ((key = [enumerator nextObject]) != nil) + responseHeaders->set (nsStringToJuce (key), + nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + } } - if (isPost && postData.getSize() > 0) + ~WebInputStream() { - [req setHTTPBody: [NSData dataWithBytes: postData.getData() - length: postData.getSize()]]; + close(); } - JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req - withCallback: callback - withContext: callbackContext]; + bool isError() const { return connection == nil; } + int64 getTotalLength() { return connection == nil ? -1 : connection->contentLength; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } - if ([s isOpen]) - return s; - - [s release]; - return 0; -} - -void juce_closeInternetFile (void* handle) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) + int read (void* buffer, int bytesToRead) { - const ScopedAutoReleasePool pool; - [s stop]; + if (finished || isError()) + { + return 0; + } + else + { + JUCE_AUTORELEASEPOOL + const int bytesRead = [connection read: static_cast (buffer) numBytes: bytesToRead]; + position += bytesRead; + + if (bytesRead == 0) + finished = true; + + return bytesRead; + } + } + + bool setPosition (int64 wantedPos) + { + if (wantedPos != position) + { + finished = false; + + if (wantedPos < position) + { + close(); + position = 0; + connection = createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + +private: + JuceURLConnection* connection; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + const int timeOutMs; + + void close() + { + [connection stop]; + [connection release]; + connection = nil; + } + + JuceURLConnection* createConnection (URL::OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext) + { + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] + 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 (":", false, false).trim()); + const String value (headerLines[i].fromFirstOccurrenceOf (":", 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: progressCallback + withContext: progressCallbackContext]; + + if ([s isOpen]) + return s; + [s release]; - } -} - -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) - { - const ScopedAutoReleasePool pool; - return [s read: (char*) buffer numBytes: bytesToRead]; + return 0; } - return 0; -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); +}; -int64 juce_getInternetFileContentLength (void* handle) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - JuceURLConnection* const s = (JuceURLConnection*) handle; + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (s != 0) - return s->contentLength; - - return -1; -} - -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0 && s->headers != 0) - { - NSEnumerator* enumerator = [s->headers keyEnumerator]; - NSString* key; - - while ((key = [enumerator nextObject]) != nil) - headers.set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [s->headers objectForKey: key])); - } -} - -int juce_seekInInternetFile (void* handle, int /*newPosition*/) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) - return [s readPosition]; - - return 0; + return wi->isError() ? 0 : wi.release(); } #endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index a66237ef1e..ca85fd4d29 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -17030,6 +17030,11 @@ private: String url, postData; StringPairArray parameters, filesToUpload, mimeTypes; + static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext, const String& headers, + const int timeOutMs, StringPairArray* responseHeaders); + JUCE_LEAK_DETECTOR (URL); }; @@ -33841,6 +33846,9 @@ public: */ double getCurrentPosition() const; + /** Returns the stream's length in seconds. */ + double getLengthInSeconds() const; + /** Returns true if the player has stopped because its input stream ran out of data. */ bool hasStreamFinished() const throw() { return inputStreamEOF; } @@ -49533,7 +49541,7 @@ private: int getNumRows() const throw(); TreeViewItem* getItemOnRow (int index) throw(); void deselectAllRecursively(); - int countSelectedItemsRecursively() const throw(); + int countSelectedItemsRecursively (int depth) const throw(); TreeViewItem* getSelectedItemWithIndex (int index) throw(); TreeViewItem* getNextVisibleItem (bool recurse) const throw(); TreeViewItem* findItemFromIdentifierString (const String& identifierString); @@ -49656,10 +49664,11 @@ public: void clearSelectedItems(); /** Returns the number of items that are currently selected. - + If maximumDepthToSearchTo is >= 0, it lets you specify a maximum depth to which the + tree will be recursed. @see getSelectedItem, clearSelectedItems */ - int getNumSelectedItems() const throw(); + int getNumSelectedItems (int maximumDepthToSearchTo = -1) const throw(); /** Returns one of the selected items in the tree. diff --git a/src/audio/audio_sources/juce_AudioTransportSource.cpp b/src/audio/audio_sources/juce_AudioTransportSource.cpp index 5481e47896..27e2e4cca8 100644 --- a/src/audio/audio_sources/juce_AudioTransportSource.cpp +++ b/src/audio/audio_sources/juce_AudioTransportSource.cpp @@ -170,6 +170,11 @@ double AudioTransportSource::getCurrentPosition() const return 0.0; } +double AudioTransportSource::getLengthInSeconds() const +{ + return getTotalLength() / sampleRate; +} + void AudioTransportSource::setNextReadPosition (int newPosition) { if (positionableSource != 0) diff --git a/src/audio/audio_sources/juce_AudioTransportSource.h b/src/audio/audio_sources/juce_AudioTransportSource.h index bef18c7a96..147cd8f642 100644 --- a/src/audio/audio_sources/juce_AudioTransportSource.h +++ b/src/audio/audio_sources/juce_AudioTransportSource.h @@ -96,6 +96,9 @@ public: */ double getCurrentPosition() const; + /** Returns the stream's length in seconds. */ + double getLengthInSeconds() const; + /** Returns true if the player has stopped because its input stream ran out of data. */ bool hasStreamFinished() const throw() { return inputStreamEOF; } diff --git a/src/audio/processors/juce_AudioProcessorGraph.cpp b/src/audio/processors/juce_AudioProcessorGraph.cpp index a29855bab7..109b3736a4 100644 --- a/src/audio/processors/juce_AudioProcessorGraph.cpp +++ b/src/audio/processors/juce_AudioProcessorGraph.cpp @@ -567,7 +567,8 @@ private: //============================================================================== AudioProcessorGraph& graph; const Array& orderedNodes; - Array nodeIds, channels, midiNodeIds; + Array channels; + Array nodeIds, midiNodeIds; //============================================================================== void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, @@ -850,7 +851,7 @@ private: return 0; } - int getBufferContaining (const int nodeId, const int outputChannel) const + int getBufferContaining (const uint32 nodeId, const int outputChannel) const { if (outputChannel == AudioProcessorGraph::midiChannelIndex) { @@ -897,7 +898,7 @@ private: bool isBufferNeededLater (int stepIndexToSearchFrom, int inputChannelOfIndexToIgnore, - const int nodeId, + const uint32 nodeId, const int outputChanIndex) const { while (stepIndexToSearchFrom < orderedNodes.size()) @@ -927,7 +928,7 @@ private: return false; } - void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) + void markBufferAsContaining (int bufferNum, uint32 nodeId, int outputIndex) { if (outputIndex == AudioProcessorGraph::midiChannelIndex) { diff --git a/src/gui/components/controls/juce_TreeView.cpp b/src/gui/components/controls/juce_TreeView.cpp index 12d94f2de3..7dc0cd0445 100644 --- a/src/gui/components/controls/juce_TreeView.cpp +++ b/src/gui/components/controls/juce_TreeView.cpp @@ -586,9 +586,9 @@ void TreeView::clearSelectedItems() rootItem->deselectAllRecursively(); } -int TreeView::getNumSelectedItems() const throw() +int TreeView::getNumSelectedItems (int maximumDepthToSearchTo) const throw() { - return (rootItem != 0) ? rootItem->countSelectedItemsRecursively() : 0; + return (rootItem != 0) ? rootItem->countSelectedItemsRecursively (maximumDepthToSearchTo) : 0; } TreeViewItem* TreeView::getSelectedItem (const int index) const throw() @@ -1623,12 +1623,13 @@ TreeViewItem* TreeViewItem::findItemRecursively (int targetY) throw() return 0; } -int TreeViewItem::countSelectedItemsRecursively() const throw() +int TreeViewItem::countSelectedItemsRecursively (int depth) const throw() { int total = isSelected() ? 1 : 0; - for (int i = subItems.size(); --i >= 0;) - total += subItems.getUnchecked(i)->countSelectedItemsRecursively(); + if (depth != 0) + for (int i = subItems.size(); --i >= 0;) + total += subItems.getUnchecked(i)->countSelectedItemsRecursively (depth - 1); return total; } @@ -1654,7 +1655,7 @@ TreeViewItem* TreeViewItem::getSelectedItemWithIndex (int index) throw() if (found != 0) return found; - index -= item->countSelectedItemsRecursively(); + index -= item->countSelectedItemsRecursively (-1); } } diff --git a/src/gui/components/controls/juce_TreeView.h b/src/gui/components/controls/juce_TreeView.h index cf3e378300..fbd1fa8b9b 100644 --- a/src/gui/components/controls/juce_TreeView.h +++ b/src/gui/components/controls/juce_TreeView.h @@ -483,7 +483,7 @@ private: int getNumRows() const throw(); TreeViewItem* getItemOnRow (int index) throw(); void deselectAllRecursively(); - int countSelectedItemsRecursively() const throw(); + int countSelectedItemsRecursively (int depth) const throw(); TreeViewItem* getSelectedItemWithIndex (int index) throw(); TreeViewItem* getNextVisibleItem (bool recurse) const throw(); TreeViewItem* findItemFromIdentifierString (const String& identifierString); @@ -610,10 +610,11 @@ public: void clearSelectedItems(); /** Returns the number of items that are currently selected. - + If maximumDepthToSearchTo is >= 0, it lets you specify a maximum depth to which the + tree will be recursed. @see getSelectedItem, clearSelectedItems */ - int getNumSelectedItems() const throw(); + int getNumSelectedItems (int maximumDepthToSearchTo = -1) const throw(); /** Returns one of the selected items in the tree. diff --git a/src/gui/components/windows/juce_DocumentWindow.cpp b/src/gui/components/windows/juce_DocumentWindow.cpp index 2f16909333..5c36a992a8 100644 --- a/src/gui/components/windows/juce_DocumentWindow.cpp +++ b/src/gui/components/windows/juce_DocumentWindow.cpp @@ -82,6 +82,14 @@ DocumentWindow::DocumentWindow (const String& title, DocumentWindow::~DocumentWindow() { + // Don't delete or remove the resizer components yourself! They're managed by the + // DocumentWindow, and you should leave them alone! You may have deleted them + // accidentally by careless use of deleteAllChildren()..? + jassert (menuBar == 0 || getIndexOfChildComponent (menuBar) >= 0); + jassert (titleBarButtons[0] == 0 || getIndexOfChildComponent (titleBarButtons[0]) >= 0); + jassert (titleBarButtons[1] == 0 || getIndexOfChildComponent (titleBarButtons[1]) >= 0); + jassert (titleBarButtons[2] == 0 || getIndexOfChildComponent (titleBarButtons[2]) >= 0); + for (int i = numElementsInArray (titleBarButtons); --i >= 0;) titleBarButtons[i] = 0; diff --git a/src/io/network/juce_URL.cpp b/src/io/network/juce_URL.cpp index 140565a33b..83adcaf8ee 100644 --- a/src/io/network/juce_URL.cpp +++ b/src/io/network/juce_URL.cpp @@ -126,6 +126,62 @@ namespace URLHelpers return url[i] == ':' ? i + 1 : 0; } + + void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData) + { + MemoryOutputStream data (postData, false); + + if (url.getFilesToUpload().size() > 0) + { + // need to upload some files, so do it as multi-part... + const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); + + headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; + + data << "--" << boundary; + + int i; + for (i = 0; i < url.getParameters().size(); ++i) + { + data << "\r\nContent-Disposition: form-data; name=\"" + << url.getParameters().getAllKeys() [i] + << "\"\r\n\r\n" + << url.getParameters().getAllValues() [i] + << "\r\n--" + << boundary; + } + + for (i = 0; i < url.getFilesToUpload().size(); ++i) + { + const File file (url.getFilesToUpload().getAllValues() [i]); + const String paramName (url.getFilesToUpload().getAllKeys() [i]); + + data << "\r\nContent-Disposition: form-data; name=\"" << paramName + << "\"; filename=\"" << file.getFileName() << "\"\r\n"; + + const String mimeType (url.getMimeTypesOfUploadFiles() + .getValue (paramName, String::empty)); + + if (mimeType.isNotEmpty()) + data << "Content-Type: " << mimeType << "\r\n"; + + data << "Content-Transfer-Encoding: binary\r\n\r\n" + << file << "\r\n--" << boundary; + } + + data << "--\r\n"; + data.flush(); + } + else + { + data << getMangledParameters (url.getParameters()) << url.getPostData(); + data.flush(); + + // just a short text attachment, so use simple url encoding.. + headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " + << postData.getSize() << "\r\n"; + } + } } const String URL::toString (const bool includeGetParameters) const @@ -201,27 +257,20 @@ const URL URL::withNewSubPath (const String& newPath) const //============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { - if (possibleURL.startsWithIgnoreCase ("http:") - || possibleURL.startsWithIgnoreCase ("ftp:")) - return true; + const char* validProtocols[] = { "http:", "ftp:", "https:" }; - if (possibleURL.startsWithIgnoreCase ("file:") - || possibleURL.containsChar ('@') - || possibleURL.endsWithChar ('.') - || (! possibleURL.containsChar ('.'))) - return false; - - if (possibleURL.startsWithIgnoreCase ("www.") - && possibleURL.substring (5).containsChar ('.')) - return true; - - const char* commonTLDs[] = { "com", "net", "org", "uk", "de", "fr", "jp" }; - - for (int i = 0; i < numElementsInArray (commonTLDs); ++i) - if ((possibleURL + "/").containsIgnoreCase ("." + String (commonTLDs[i]) + "/")) + for (int i = 0; i < numElementsInArray (validProtocols); ++i) + if (possibleURL.startsWithIgnoreCase (validProtocols[i])) return true; - return false; + if (possibleURL.containsChar ('@') + || possibleURL.containsChar (' ')) + return false; + + const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false) + .fromLastOccurrenceOf (".", false, false)); + + return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3; } bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) @@ -234,194 +283,6 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) } //============================================================================== -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& optionalPostData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs); - -void juce_closeInternetFile (void* handle); -int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead); -int juce_seekInInternetFile (void* handle, int newPosition); -int64 juce_getInternetFileContentLength (void* handle); -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers); - - -//============================================================================== -class WebInputStream : public InputStream -{ -public: - //============================================================================== - WebInputStream (const URL& url, - const bool isPost_, - URL::OpenStreamProgressCallback* const progressCallback_, - void* const progressCallbackContext_, - const String& extraHeaders, - const int timeOutMs_, - StringPairArray* const responseHeaders) - : position (0), - finished (false), - isPost (isPost_), - progressCallback (progressCallback_), - progressCallbackContext (progressCallbackContext_), - timeOutMs (timeOutMs_) - { - server = url.toString (! isPost); - - if (isPost_) - createHeadersAndPostData (url); - - headers += extraHeaders; - - if (! headers.endsWithChar ('\n')) - headers << "\r\n"; - - handle = juce_openInternetFile (server, headers, postData, isPost, - progressCallback_, progressCallbackContext_, - timeOutMs); - - if (responseHeaders != 0) - juce_getInternetFileHeaders (handle, *responseHeaders); - } - - ~WebInputStream() - { - juce_closeInternetFile (handle); - } - - //============================================================================== - bool isError() const { return handle == 0; } - int64 getTotalLength() { return juce_getInternetFileContentLength (handle); } - bool isExhausted() { return finished; } - int64 getPosition() { return position; } - - int read (void* dest, int bytes) - { - if (finished || isError()) - { - return 0; - } - else - { - const int bytesRead = juce_readFromInternetFile (handle, dest, bytes); - position += bytesRead; - - if (bytesRead == 0) - finished = true; - - return bytesRead; - } - } - - bool setPosition (int64 wantedPos) - { - if (wantedPos != position) - { - finished = false; - - const int actualPos = juce_seekInInternetFile (handle, (int) wantedPos); - - if (actualPos == wantedPos) - { - position = wantedPos; - } - else - { - if (wantedPos < position) - { - juce_closeInternetFile (handle); - - position = 0; - finished = false; - - handle = juce_openInternetFile (server, headers, postData, isPost, - progressCallback, progressCallbackContext, - timeOutMs); - } - - skipNextBytes (wantedPos - position); - } - } - - return true; - } - - //============================================================================== -private: - String server, headers; - MemoryBlock postData; - int64 position; - bool finished; - const bool isPost; - void* handle; - URL::OpenStreamProgressCallback* const progressCallback; - void* const progressCallbackContext; - const int timeOutMs; - - void createHeadersAndPostData (const URL& url) - { - MemoryOutputStream data (postData, false); - - if (url.getFilesToUpload().size() > 0) - { - // need to upload some files, so do it as multi-part... - const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); - - headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; - - data << "--" << boundary; - - int i; - for (i = 0; i < url.getParameters().size(); ++i) - { - data << "\r\nContent-Disposition: form-data; name=\"" - << url.getParameters().getAllKeys() [i] - << "\"\r\n\r\n" - << url.getParameters().getAllValues() [i] - << "\r\n--" - << boundary; - } - - for (i = 0; i < url.getFilesToUpload().size(); ++i) - { - const File file (url.getFilesToUpload().getAllValues() [i]); - const String paramName (url.getFilesToUpload().getAllKeys() [i]); - - data << "\r\nContent-Disposition: form-data; name=\"" << paramName - << "\"; filename=\"" << file.getFileName() << "\"\r\n"; - - const String mimeType (url.getMimeTypesOfUploadFiles() - .getValue (paramName, String::empty)); - - if (mimeType.isNotEmpty()) - data << "Content-Type: " << mimeType << "\r\n"; - - data << "Content-Transfer-Encoding: binary\r\n\r\n" - << file << "\r\n--" << boundary; - } - - data << "--\r\n"; - data.flush(); - } - else - { - data << URLHelpers::getMangledParameters (url.getParameters()) - << url.getPostData(); - - data.flush(); - - // just a short text attachment, so use simple url encoding.. - headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - + String ((unsigned int) postData.getSize()) - + "\r\n"; - } - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); -}; - InputStream* URL::createInputStream (const bool usePostCommand, OpenStreamProgressCallback* const progressCallback, void* const progressCallbackContext, @@ -429,11 +290,20 @@ InputStream* URL::createInputStream (const bool usePostCommand, const int timeOutMs, StringPairArray* const responseHeaders) const { - ScopedPointer wi (new WebInputStream (*this, usePostCommand, - progressCallback, progressCallbackContext, - extraHeaders, timeOutMs, responseHeaders)); + String headers; + MemoryBlock postData; - return wi->isError() ? 0 : wi.release(); + if (usePostCommand) + URLHelpers::createHeadersAndPostData (*this, headers, postData); + + headers += extraHeaders; + + if (! headers.endsWithChar ('\n')) + headers << "\r\n"; + + return createNativeStream (toString (! usePostCommand), usePostCommand, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders); } //============================================================================== @@ -582,4 +452,5 @@ bool URL::launchInDefaultBrowser() const return PlatformUtilities::openDocument (u, String::empty); } + END_JUCE_NAMESPACE diff --git a/src/io/network/juce_URL.h b/src/io/network/juce_URL.h index 52220e0ff9..b834e341e0 100644 --- a/src/io/network/juce_URL.h +++ b/src/io/network/juce_URL.h @@ -291,6 +291,11 @@ private: String url, postData; StringPairArray parameters, filesToUpload, mimeTypes; + static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext, const String& headers, + const int timeOutMs, StringPairArray* responseHeaders); + JUCE_LEAK_DETECTOR (URL); }; diff --git a/src/native/juce_linux_NativeCode.cpp b/src/native/juce_linux_NativeCode.cpp index 5ca65e9750..745b530bdd 100644 --- a/src/native/juce_linux_NativeCode.cpp +++ b/src/native/juce_linux_NativeCode.cpp @@ -34,6 +34,8 @@ #if JUCE_LINUX +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 #include "linux/juce_linux_NativeIncludes.h" BEGIN_JUCE_NAMESPACE diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index dec8152ca0..6271331df6 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -34,6 +34,8 @@ #if JUCE_MAC || JUCE_IOS +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 #include "mac/juce_mac_NativeIncludes.h" BEGIN_JUCE_NAMESPACE diff --git a/src/native/juce_win32_NativeCode.cpp b/src/native/juce_win32_NativeCode.cpp index 947f84754b..28574c2821 100644 --- a/src/native/juce_win32_NativeCode.cpp +++ b/src/native/juce_win32_NativeCode.cpp @@ -34,6 +34,8 @@ #if JUCE_WINDOWS +#undef JUCE_BUILD_NATIVE +#define JUCE_BUILD_NATIVE 1 #include "windows/juce_win32_NativeIncludes.h" #include "../core/juce_StandardHeader.h" diff --git a/src/native/linux/juce_linux_Network.cpp b/src/native/linux/juce_linux_Network.cpp index 6e255abe09..1ce62dfa17 100644 --- a/src/native/linux/juce_linux_Network.cpp +++ b/src/native/linux/juce_linux_Network.cpp @@ -68,34 +68,116 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd return false; } + //============================================================================== -/** A HTTP input stream that uses sockets. - */ -class JUCE_HTTPSocketStream +class WebInputStream : public InputStream { public: //============================================================================== - JUCE_HTTPSocketStream() - : readPosition (0), - socketHandle (-1), - levelsOfRedirection (0), - timeoutSeconds (15) + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : socketHandle (-1), levelsOfRedirection (0), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { + createConnection (progressCallback, progressCallbackContext); + + if (responseHeaders != 0 && ! isError()) + { + for (int i = 0; i < headerLines.size(); ++i) + { + const String& headersEntry = headerLines[i]; + const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); + const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + } } - ~JUCE_HTTPSocketStream() + ~WebInputStream() { closeSocket(); } //============================================================================== - bool open (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) + bool isError() const { return socketHandle < 0; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } + + int64 getTotalLength() + { + jassertfalse; //xxx to do + return -1; + } + + int read (void* buffer, int bytesToRead) + { + if (finished || isError()) + return 0; + + fd_set readbits; + FD_ZERO (&readbits); + FD_SET (socketHandle, &readbits); + + struct timeval tv; + tv.tv_sec = jmax (1, timeOutMs / 1000); + tv.tv_usec = 0; + + if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) + return 0; // (timeout) + + const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); + if (bytesRead == 0) + finished = true; + position += bytesRead; + return bytesRead; + } + + bool setPosition (int64 wantedPos) + { + if (isError()) + return false; + + if (wantedPos != position) + { + finished = false; + + if (wantedPos < position) + { + closeSocket(); + position = 0; + createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + + //============================================================================== +private: + int socketHandle, levelsOfRedirection; + StringArray headerLines; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + const int timeOutMs; + + void closeSocket() + { + if (socketHandle >= 0) + close (socketHandle); + + socketHandle = -1; + levelsOfRedirection = 0; + } + + void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { closeSocket(); @@ -110,9 +192,8 @@ public: String hostName, hostPath; int hostPort; - - if (! decomposeURL (url, hostName, hostPath, hostPort)) - return false; + if (! decomposeURL (address, hostName, hostPath, hostPort)) + return; const struct hostent* host = 0; int port = 0; @@ -124,7 +205,7 @@ public: if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) - return false; + return; host = gethostbyname (proxyName.toUTF8()); port = proxyPort; @@ -136,64 +217,47 @@ public: } if (host == 0) - return false; + return; - struct sockaddr_in address; - zerostruct (address); - memcpy (&address.sin_addr, host->h_addr, host->h_length); - address.sin_family = host->h_addrtype; - address.sin_port = htons (port); - - socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); - - if (socketHandle == -1) - return false; - - int receiveBufferSize = 16384; - setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); - setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); - -#if JUCE_MAC - setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); -#endif - - if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) { - closeSocket(); - return false; - } + struct sockaddr_in socketAddress; + zerostruct (socketAddress); + memcpy (&socketAddress.sin_addr, host->h_addr, host->h_length); + socketAddress.sin_family = host->h_addrtype; + socketAddress.sin_port = htons (port); - const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, - hostPath, url, headers, postData, isPost)); - size_t totalHeaderSent = 0; + socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); - while (totalHeaderSent < requestHeader.getSize()) - { - if (Time::getMillisecondCounter() > timeOutTime) + if (socketHandle == -1) + return; + + int receiveBufferSize = 16384; + setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); + setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); + + #if JUCE_MAC + setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); + #endif + + if (connect (socketHandle, (struct sockaddr*) &socketAddress, sizeof (socketAddress)) == -1) { closeSocket(); - return false; - } - - const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); - - if (send (socketHandle, ((const char*) requestHeader.getData()) + totalHeaderSent, numToSend, 0) - != numToSend) - { - closeSocket(); - return false; - } - - totalHeaderSent += numToSend; - - if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) - { - closeSocket(); - return false; + return; } } - const String responseHeader (readResponse (timeOutTime)); + { + const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, + hostPath, address, headers, postData, isPost)); + + if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) + { + closeSocket(); + return; + } + } + + const String responseHeader (readResponse (socketHandle, timeOutTime)); if (responseHeader.isNotEmpty()) { @@ -208,113 +272,43 @@ public: String location (findHeaderItem (headerLines, "Location:")); - if (statusCode >= 300 && statusCode < 400 - && location.isNotEmpty()) + if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) { if (! location.startsWithIgnoreCase ("http://")) location = "http://" + location; - if (levelsOfRedirection++ < 3) - return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); + if (++levelsOfRedirection <= 3) + { + address = location; + createConnection (progressCallback, progressCallbackContext); + return; + } } else { levelsOfRedirection = 0; - return true; + return; } } closeSocket(); - return false; } //============================================================================== - int read (void* buffer, int bytesToRead) - { - fd_set readbits; - FD_ZERO (&readbits); - FD_SET (socketHandle, &readbits); - - struct timeval tv; - tv.tv_sec = timeoutSeconds; - tv.tv_usec = 0; - - if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) - return 0; // (timeout) - - const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); - readPosition += bytesRead; - return bytesRead; - } - - //============================================================================== - int readPosition; - StringArray headerLines; - - //============================================================================== -private: - int socketHandle, levelsOfRedirection; - const int timeoutSeconds; - - //============================================================================== - void closeSocket() - { - if (socketHandle >= 0) - close (socketHandle); - - socketHandle = -1; - } - - const MemoryBlock createRequestHeader (const String& hostName, - const int hostPort, - const String& proxyName, - const int proxyPort, - const String& hostPath, - const String& originalURL, - const String& headers, - const MemoryBlock& postData, - const bool isPost) - { - String header (isPost ? "POST " : "GET "); - - if (proxyName.isEmpty()) - { - header << hostPath << " HTTP/1.0\r\nHost: " - << hostName << ':' << hostPort; - } - else - { - header << originalURL << " HTTP/1.0\r\nHost: " - << proxyName << ':' << proxyPort; - } - - header << "\r\nUser-Agent: JUCE/" - << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION - << "\r\nConnection: Close\r\nContent-Length: " - << postData.getSize() << "\r\n" - << headers << "\r\n"; - - MemoryBlock mb; - mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); - mb.append (postData.getData(), postData.getSize()); - - return mb; - } - - const String readResponse (const uint32 timeOutTime) + static const String readResponse (const int socketHandle, const uint32 timeOutTime) { int bytesRead = 0, numConsecutiveLFs = 0; MemoryBlock buffer (1024, true); while (numConsecutiveLFs < 2 && bytesRead < 32768 - && Time::getMillisecondCounter() <= timeOutTime) + && Time::getMillisecondCounter() <= timeOutTime) { fd_set readbits; FD_ZERO (&readbits); FD_SET (socketHandle, &readbits); struct timeval tv; - tv.tv_sec = timeoutSeconds; + tv.tv_sec = jmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); tv.tv_usec = 0; if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) @@ -343,9 +337,62 @@ private: return String::empty; } - //============================================================================== - static bool decomposeURL (const String& url, - String& host, String& path, int& port) + static const MemoryBlock createRequestHeader (const String& hostName, const int hostPort, + const String& proxyName, const int proxyPort, + const String& hostPath, const String& originalURL, + const String& headers, const MemoryBlock& postData, + const bool isPost) + { + String header (isPost ? "POST " : "GET "); + + if (proxyName.isEmpty()) + { + header << hostPath << " HTTP/1.0\r\nHost: " + << hostName << ':' << hostPort; + } + else + { + header << originalURL << " HTTP/1.0\r\nHost: " + << proxyName << ':' << proxyPort; + } + + header << "\r\nUser-Agent: JUCE/" << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION + << "\r\nConnection: Close\r\nContent-Length: " + << postData.getSize() << "\r\n" + << headers << "\r\n"; + + MemoryBlock mb; + mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); + mb.append (postData.getData(), postData.getSize()); + + return mb; + } + + static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + { + size_t totalHeaderSent = 0; + + while (totalHeaderSent < requestHeader.getSize()) + { + if (Time::getMillisecondCounter() > timeOutTime) + return false; + + const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); + + if (send (socketHandle, static_cast (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend) + return false; + + totalHeaderSent += numToSend; + + if (progressCallback != 0 && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize())) + return false; + } + + return true; + } + + static bool decomposeURL (const String& url, String& host, String& path, int& port) { if (! url.startsWithIgnoreCase ("http://")) return false; @@ -382,7 +429,6 @@ private: return true; } - //============================================================================== static const String findHeaderItem (const StringArray& lines, const String& itemName) { for (int i = 0; i < lines.size(); ++i) @@ -392,73 +438,19 @@ private: return String::empty; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JUCE_HTTPSocketStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); }; -//============================================================================== -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - ScopedPointer s (new JUCE_HTTPSocketStream()); + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) - return s.release(); - - return 0; + return wi->isError() ? 0 : wi.release(); } -void juce_closeInternetFile (void* handle) -{ - delete static_cast (handle); -} - -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - return s != 0 ? s->read (buffer, bytesToRead) : 0; -} - -int64 juce_getInternetFileContentLength (void* handle) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - if (s != 0) - { - //xxx todo - jassertfalse - } - - return -1; -} - -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - if (s != 0) - { - for (int i = 0; i < s->headerLines.size(); ++i) - { - const String& headersEntry = s->headerLines[i]; - const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); - const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (headers [key]); - headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); - } - } -} - -int juce_seekInInternetFile (void* handle, int newPosition) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - return s != 0 ? s->readPosition : 0; -} #endif diff --git a/src/native/mac/juce_mac_NativeIncludes.h b/src/native/mac/juce_mac_NativeIncludes.h index d8fdebc62e..e1102dd3d9 100644 --- a/src/native/mac/juce_mac_NativeIncludes.h +++ b/src/native/mac/juce_mac_NativeIncludes.h @@ -53,17 +53,21 @@ #else #import #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #import - #include + #if JUCE_BUILD_NATIVE + #import + #import + #import + #import + #import + #import + #import + #import + #import + #import + #endif + #if JUCE_BUILD_MISC && (JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_AU) + #include + #endif #include #endif diff --git a/src/native/mac/juce_mac_Network.mm b/src/native/mac/juce_mac_Network.mm index 24c1d0ea47..e7ea308aae 100644 --- a/src/native/mac/juce_mac_Network.mm +++ b/src/native/mac/juce_mac_Network.mm @@ -142,8 +142,6 @@ using namespace JUCE_NAMESPACE; class JuceURLConnectionMessageThread : public Thread { - JuceURLConnection* owner; - public: JuceURLConnectionMessageThread (JuceURLConnection* owner_) : Thread ("http connection"), @@ -166,6 +164,9 @@ public: [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } } + +private: + JuceURLConnection* owner; }; @@ -324,119 +325,155 @@ public: } @end + BEGIN_JUCE_NAMESPACE - -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) +//============================================================================== +class WebInputStream : public InputStream { - const ScopedAutoReleasePool 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) +public: + //============================================================================== + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : connection (nil), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); - const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); + JUCE_AUTORELEASEPOOL + connection = createConnection (progressCallback, progressCallbackContext); - if (key.isNotEmpty() && value.isNotEmpty()) - [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; + if (responseHeaders != 0 && connection != 0 && connection->headers != 0) + { + NSEnumerator* enumerator = [connection->headers keyEnumerator]; + NSString* key; + + while ((key = [enumerator nextObject]) != nil) + responseHeaders->set (nsStringToJuce (key), + nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + } } - if (isPost && postData.getSize() > 0) + ~WebInputStream() { - [req setHTTPBody: [NSData dataWithBytes: postData.getData() - length: postData.getSize()]]; + close(); } - JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req - withCallback: callback - withContext: callbackContext]; + //============================================================================== + bool isError() const { return connection == nil; } + int64 getTotalLength() { return connection == nil ? -1 : connection->contentLength; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } - if ([s isOpen]) - return s; - - [s release]; - return 0; -} - -void juce_closeInternetFile (void* handle) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) + int read (void* buffer, int bytesToRead) { - const ScopedAutoReleasePool pool; - [s stop]; + if (finished || isError()) + { + return 0; + } + else + { + JUCE_AUTORELEASEPOOL + const int bytesRead = [connection read: static_cast (buffer) numBytes: bytesToRead]; + position += bytesRead; + + if (bytesRead == 0) + finished = true; + + return bytesRead; + } + } + + bool setPosition (int64 wantedPos) + { + if (wantedPos != position) + { + finished = false; + + if (wantedPos < position) + { + close(); + position = 0; + connection = createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + + //============================================================================== +private: + JuceURLConnection* connection; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + const int timeOutMs; + + void close() + { + [connection stop]; + [connection release]; + connection = nil; + } + + JuceURLConnection* createConnection (URL::OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext) + { + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] + 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 (":", false, false).trim()); + const String value (headerLines[i].fromFirstOccurrenceOf (":", 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: progressCallback + withContext: progressCallbackContext]; + + if ([s isOpen]) + return s; + [s release]; - } -} - -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) - { - const ScopedAutoReleasePool pool; - return [s read: (char*) buffer numBytes: bytesToRead]; + return 0; } - return 0; -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); +}; -int64 juce_getInternetFileContentLength (void* handle) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - JuceURLConnection* const s = (JuceURLConnection*) handle; + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (s != 0) - return s->contentLength; - - return -1; + return wi->isError() ? 0 : wi.release(); } -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0 && s->headers != 0) - { - NSEnumerator* enumerator = [s->headers keyEnumerator]; - NSString* key; - - while ((key = [enumerator nextObject]) != nil) - headers.set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [s->headers objectForKey: key])); - } -} - - -int juce_seekInInternetFile (void* handle, int /*newPosition*/) -{ - JuceURLConnection* const s = (JuceURLConnection*) handle; - - if (s != 0) - return [s readPosition]; - - return 0; -} #endif diff --git a/src/native/windows/juce_win32_NativeIncludes.h b/src/native/windows/juce_win32_NativeIncludes.h index 51f5ecf29d..6ceb41ba49 100644 --- a/src/native/windows/juce_win32_NativeIncludes.h +++ b/src/native/windows/juce_win32_NativeIncludes.h @@ -82,7 +82,7 @@ #undef PACKED //============================================================================== -#if JUCE_ASIO +#if JUCE_ASIO && JUCE_BUILD_NATIVE /* This is very frustrating - we only need to use a handful of definitions from a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy @@ -112,7 +112,7 @@ #endif //============================================================================== -#if JUCE_USE_CDBURNER +#if JUCE_USE_CDBURNER && JUCE_BUILD_NATIVE /* You'll need the Platform SDK for these headers - if you don't have it and don't need to use CD-burning, then you might just want to disable the JUCE_USE_CDBURNER @@ -123,7 +123,7 @@ #endif //============================================================================== -#if JUCE_USE_CAMERA +#if JUCE_USE_CAMERA && JUCE_BUILD_NATIVE /* If you're using the camera classes, you'll need access to a few DirectShow headers. @@ -147,7 +147,7 @@ #endif //============================================================================== -#if JUCE_WASAPI +#if JUCE_WASAPI && JUCE_BUILD_NATIVE #include #include #include @@ -183,7 +183,7 @@ #pragma warning (pop) #endif -#if JUCE_DIRECT2D +#if JUCE_DIRECT2D && JUCE_BUILD_NATIVE #include #include #endif diff --git a/src/native/windows/juce_win32_Network.cpp b/src/native/windows/juce_win32_Network.cpp index 02b277d2dc..8bd0e24369 100644 --- a/src/native/windows/juce_win32_Network.cpp +++ b/src/native/windows/juce_win32_Network.cpp @@ -83,243 +83,283 @@ private: }; #endif -void* juce_openInternetFile (const String& url, - const String& headers, - const MemoryBlock& postData, - const bool isPost, - URL::OpenStreamProgressCallback* callback, - void* callbackContext, - int timeOutMs) + +//============================================================================== +class WebInputStream : public InputStream { - if (sessionHandle == 0) - sessionHandle = InternetOpen (_T("juce"), - INTERNET_OPEN_TYPE_PRECONFIG, - 0, 0, 0); - - if (sessionHandle != 0) +public: + //============================================================================== + WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, + URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) + : connection (0), request (0), + address (address_), headers (headers_), postData (postData_), position (0), + finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - // break up the url.. - TCHAR file[1024], server[1024]; + createConnection (progressCallback, progressCallbackContext); - URL_COMPONENTS uc; - zerostruct (uc); - - uc.dwStructSize = sizeof (uc); - uc.dwUrlPathLength = sizeof (file); - uc.dwHostNameLength = sizeof (server); - uc.lpszUrlPath = file; - uc.lpszHostName = server; - - if (InternetCrackUrl (url, 0, 0, &uc)) + if (responseHeaders != 0 && ! isError()) { - int disable = 1; - InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); - - if (timeOutMs == 0) - timeOutMs = 30000; - else if (timeOutMs < 0) - timeOutMs = -1; - - InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs)); - - const bool isFtp = url.startsWithIgnoreCase ("ftp:"); - -#if WORKAROUND_TIMEOUT_BUG - HINTERNET connection = 0; + DWORD bufferSizeBytes = 4096; + for (;;) { - InternetConnectThread connectThread (uc, connection, isFtp); - connectThread.wait (timeOutMs); + HeapBlock buffer ((size_t) bufferSizeBytes); - if (connection == 0) + if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { - InternetCloseHandle (sessionHandle); - sessionHandle = 0; - } - } -#else - HINTERNET connection = InternetConnect (sessionHandle, - uc.lpszHostName, - uc.nPort, - _T(""), _T(""), - isFtp ? INTERNET_SERVICE_FTP - : INTERNET_SERVICE_HTTP, - 0, 0); -#endif + StringArray headersArray; + headersArray.addLines (reinterpret_cast (buffer.getData())); - if (connection != 0) - { - if (isFtp) - { - HINTERNET request = FtpOpenFile (connection, - uc.lpszUrlPath, - GENERIC_READ, - FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, - 0); - - ConnectionAndRequestStruct* const result = new ConnectionAndRequestStruct(); - result->connection = connection; - result->request = request; - return result; - } - else - { - const TCHAR* mimeTypes[] = { _T("*/*"), 0 }; - - DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; - - if (url.startsWithIgnoreCase ("https:")) - flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - - // IE7 seems to automatically work out when it's https) - - HINTERNET request = HttpOpenRequest (connection, - isPost ? _T("POST") - : _T("GET"), - uc.lpszUrlPath, - 0, 0, mimeTypes, flags, 0); - - if (request != 0) + for (int i = 0; i < headersArray.size(); ++i) { - INTERNET_BUFFERS buffers; - zerostruct (buffers); - buffers.dwStructSize = sizeof (INTERNET_BUFFERS); - buffers.lpcszHeader = (LPCTSTR) headers; - buffers.dwHeadersLength = headers.length(); - buffers.dwBufferTotal = (DWORD) postData.getSize(); - ConnectionAndRequestStruct* result = 0; + const String& header = headersArray[i]; + const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String value (header.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); - if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + + break; + } + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; + } + + } + } + + ~WebInputStream() + { + close(); + } + + //============================================================================== + bool isError() const { return request == 0; } + bool isExhausted() { return finished; } + int64 getPosition() { return position; } + + int64 getTotalLength() + { + if (! isError()) + { + DWORD index = 0, result = 0, size = sizeof (result); + + if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) + return (int64) result; + } + + return -1; + } + + int read (void* buffer, int bytesToRead) + { + DWORD bytesRead = 0; + + if (! (finished || isError())) + { + InternetReadFile (request, buffer, bytesToRead, &bytesRead); + position += bytesRead; + + if (bytesRead == 0) + finished = true; + } + + return (int) bytesRead; + } + + bool setPosition (int64 wantedPos) + { + if (isError()) + return false; + + if (wantedPos != position) + { + finished = false; + position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0); + + if (position == wantedPos) + return true; + + if (wantedPos < position) + { + close(); + position = 0; + createConnection (0, 0); + } + + skipNextBytes (wantedPos - position); + } + + return true; + } + +private: + //============================================================================== + HINTERNET connection, request; + String address, headers; + MemoryBlock postData; + int64 position; + bool finished; + const bool isPost; + int timeOutMs; + + void close() + { + if (request != 0) + { + InternetCloseHandle (request); + request = 0; + } + + if (connection != 0) + { + InternetCloseHandle (connection); + connection = 0; + } + } + + void createConnection (URL::OpenStreamProgressCallback* progressCallback, + void* progressCallbackContext) + { + static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0); + + close(); + + if (sessionHandle != 0) + { + // break up the url.. + TCHAR file[1024], server[1024]; + + URL_COMPONENTS uc; + zerostruct (uc); + uc.dwStructSize = sizeof (uc); + uc.dwUrlPathLength = sizeof (file); + uc.dwHostNameLength = sizeof (server); + uc.lpszUrlPath = file; + uc.lpszHostName = server; + + if (InternetCrackUrl (address, 0, 0, &uc)) + { + int disable = 1; + InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable)); + + if (timeOutMs == 0) + timeOutMs = 30000; + else if (timeOutMs < 0) + timeOutMs = -1; + + InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs)); + + const bool isFtp = address.startsWithIgnoreCase ("ftp:"); + + #if WORKAROUND_TIMEOUT_BUG + connection = 0; + + { + InternetConnectThread connectThread (uc, connection, isFtp); + connectThread.wait (timeOutMs); + + if (connection == 0) + { + InternetCloseHandle (sessionHandle); + sessionHandle = 0; + } + } + #else + connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, + _T(""), _T(""), + isFtp ? INTERNET_SERVICE_FTP + : INTERNET_SERVICE_HTTP, + 0, 0); + #endif + + if (connection != 0) + { + if (isFtp) + { + request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ, + FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0); + } + else + { + const TCHAR* mimeTypes[] = { _T("*/*"), 0 }; + + DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; + + if (address.startsWithIgnoreCase ("https:")) + flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 - + // IE7 seems to automatically work out when it's https) + + request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"), + uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0); + + if (request != 0) { - int bytesSent = 0; + INTERNET_BUFFERS buffers; + zerostruct (buffers); + buffers.dwStructSize = sizeof (INTERNET_BUFFERS); + buffers.lpcszHeader = static_cast (headers); + buffers.dwHeadersLength = headers.length(); + buffers.dwBufferTotal = (DWORD) postData.getSize(); + ConnectionAndRequestStruct* result = 0; - for (;;) + if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0)) { - const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); - DWORD bytesDone = 0; + int bytesSent = 0; - if (bytesToDo > 0 - && ! InternetWriteFile (request, - static_cast (postData.getData()) + bytesSent, - bytesToDo, &bytesDone)) + for (;;) { - break; - } + const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent); + DWORD bytesDone = 0; - if (bytesToDo == 0 || (int) bytesDone < bytesToDo) - { - result = new ConnectionAndRequestStruct(); - result->connection = connection; - result->request = request; - - if (! HttpEndRequest (request, 0, 0, 0)) + if (bytesToDo > 0 + && ! InternetWriteFile (request, + static_cast (postData.getData()) + bytesSent, + bytesToDo, &bytesDone)) + { break; + } - return result; + if (bytesToDo == 0 || (int) bytesDone < bytesToDo) + { + if (HttpEndRequest (request, 0, 0, 0)) + return; + + break; + } + + bytesSent += bytesDone; + + if (progressCallback != 0 && ! progressCallback (progressCallbackContext, bytesSent, postData.getSize())) + break; } - - bytesSent += bytesDone; - - if (callback != 0 && ! callback (callbackContext, bytesSent, postData.getSize())) - break; } } - InternetCloseHandle (request); + close(); } - - InternetCloseHandle (connection); } } } } - return 0; -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); +}; -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, + OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, + const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - DWORD bytesRead = 0; - const ConnectionAndRequestStruct* const crs = static_cast (handle); + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); - if (crs != 0) - InternetReadFile (crs->request, - buffer, bytesToRead, - &bytesRead); - - return bytesRead; + return wi->isError() ? 0 : wi.release(); } -int juce_seekInInternetFile (void* handle, int newPosition) -{ - if (handle != 0) - { - const ConnectionAndRequestStruct* const crs = static_cast (handle); - return InternetSetFilePointer (crs->request, newPosition, 0, FILE_BEGIN, 0); - } - - return -1; -} - -int64 juce_getInternetFileContentLength (void* handle) -{ - const ConnectionAndRequestStruct* const crs = static_cast (handle); - - if (crs != 0) - { - DWORD index = 0, result = 0, size = sizeof (result); - - if (HttpQueryInfo (crs->request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index)) - return (int64) result; - } - - return -1; -} - -void juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - const ConnectionAndRequestStruct* const crs = static_cast (handle); - - if (crs != 0) - { - DWORD bufferSizeBytes = 4096; - - for (;;) - { - HeapBlock buffer ((size_t) bufferSizeBytes); - - if (HttpQueryInfo (crs->request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) - { - StringArray headersArray; - headersArray.addLines (reinterpret_cast (buffer.getData())); - - for (int i = 0; i < headersArray.size(); ++i) - { - const String& header = headersArray[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); - const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue (headers [key]); - - headers.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); - } - - break; - } - - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; - } - } -} - -void juce_closeInternetFile (void* handle) -{ - if (handle != 0) - { - ScopedPointer crs (static_cast (handle)); - InternetCloseHandle (crs->request); - InternetCloseHandle (crs->connection); - } -} //============================================================================== namespace MACAddressHelpers diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 765b7aff26..55054818fc 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -1946,8 +1946,7 @@ private: { const Rectangle r (component->getParentMonitorArea()); - SetWindowPos (hwnd, 0, - r.getX(), r.getY(), r.getWidth(), r.getHeight(), + SetWindowPos (hwnd, 0, r.getX(), r.getY(), r.getWidth(), r.getHeight(), SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING); } } @@ -1959,7 +1958,10 @@ public: Win32ComponentPeer* const peer = getOwnerOfWindow (h); if (peer != 0) + { + jassert (isValidPeer (peer)); return peer->peerWindowProc (h, message, wParam, lParam); + } return DefWindowProcW (h, message, wParam, lParam); } @@ -1990,325 +1992,318 @@ private: LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) { - if (isValidPeer (this)) + switch (message) { - switch (message) - { - //============================================================================== - case WM_NCHITTEST: - if ((styleFlags & windowIgnoresMouseClicks) != 0) - return HTTRANSPARENT; - else if (! hasTitleBar()) - return HTCLIENT; + //============================================================================== + case WM_NCHITTEST: + if ((styleFlags & windowIgnoresMouseClicks) != 0) + return HTTRANSPARENT; + else if (! hasTitleBar()) + return HTCLIENT; - break; + break; - //============================================================================== - case WM_PAINT: + //============================================================================== + case WM_PAINT: + handlePaintMessage(); + return 0; + + case WM_NCPAINT: + if (wParam != 1) handlePaintMessage(); - return 0; - - case WM_NCPAINT: - if (wParam != 1) - handlePaintMessage(); - - if (hasTitleBar()) - break; - - return 0; - - case WM_ERASEBKGND: - case WM_NCCALCSIZE: - if (hasTitleBar()) - break; - - return 1; - - //============================================================================== - case WM_MOUSEMOVE: - doMouseMove (getPointFromLParam (lParam)); - return 0; - - case WM_MOUSELEAVE: - doMouseExit(); - return 0; - - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - doMouseDown (getPointFromLParam (lParam), wParam); - return 0; - - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - doMouseUp (getPointFromLParam (lParam), wParam); - return 0; - - case WM_CAPTURECHANGED: - doCaptureChanged(); - return 0; - - case WM_NCMOUSEMOVE: - if (hasTitleBar()) - break; - - return 0; - - case 0x020A: /* WM_MOUSEWHEEL */ - doMouseWheel (getCurrentMousePos(), wParam, true); - return 0; - - case 0x020E: /* WM_MOUSEHWHEEL */ - doMouseWheel (getCurrentMousePos(), wParam, false); - return 0; - - //============================================================================== - case WM_SIZING: - return handleSizeConstraining ((RECT*) lParam, wParam); - - case WM_WINDOWPOSCHANGING: - return handlePositionChanging ((WINDOWPOS*) lParam); - - case WM_WINDOWPOSCHANGED: - { - const Point pos (getCurrentMousePos()); - if (contains (pos, false)) - doMouseEvent (pos); - } - - handleMovedOrResized(); - - if (dontRepaint) - break; // needed for non-accelerated openGL windows to draw themselves correctly.. - - return 0; - - //============================================================================== - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (doKeyDown (wParam)) - return 0; + if (hasTitleBar()) break; - case WM_KEYUP: - case WM_SYSKEYUP: - if (doKeyUp (wParam)) - return 0; + return 0; + case WM_ERASEBKGND: + case WM_NCCALCSIZE: + if (hasTitleBar()) break; - case WM_CHAR: - if (doKeyChar ((int) wParam, lParam)) - return 0; + return 1; + //============================================================================== + case WM_MOUSEMOVE: + doMouseMove (getPointFromLParam (lParam)); + return 0; + + case WM_MOUSELEAVE: + doMouseExit(); + return 0; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + doMouseDown (getPointFromLParam (lParam), wParam); + return 0; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + doMouseUp (getPointFromLParam (lParam), wParam); + return 0; + + case WM_CAPTURECHANGED: + doCaptureChanged(); + return 0; + + case WM_NCMOUSEMOVE: + if (hasTitleBar()) break; - case WM_APPCOMMAND: - if (doAppCommand (lParam)) - return TRUE; + return 0; - break; + case 0x020A: /* WM_MOUSEWHEEL */ + case 0x020E: /* WM_MOUSEHWHEEL */ + doMouseWheel (getCurrentMousePos(), wParam, message == 0x020A); + return 0; - //============================================================================== - case WM_SETFOCUS: - updateKeyModifiers(); - handleFocusGain(); - break; + //============================================================================== + case WM_SIZING: + return handleSizeConstraining ((RECT*) lParam, wParam); - case WM_KILLFOCUS: - if (hasCreatedCaret) - { - hasCreatedCaret = false; - DestroyCaret(); - } + case WM_WINDOWPOSCHANGING: + return handlePositionChanging ((WINDOWPOS*) lParam); - handleFocusLoss(); - break; + case WM_WINDOWPOSCHANGED: + { + const Point pos (getCurrentMousePos()); + if (contains (pos, false)) + doMouseEvent (pos); + } - case WM_ACTIVATEAPP: - // Windows does weird things to process priority when you swap apps, - // so this forces an update when the app is brought to the front - if (wParam != FALSE) - juce_repeatLastProcessPriority(); - else - Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus + handleMovedOrResized(); - juce_CheckCurrentlyFocusedTopLevelWindow(); - modifiersAtLastCallback = -1; + if (dontRepaint) + break; // needed for non-accelerated openGL windows to draw themselves correctly.. + + return 0; + + //============================================================================== + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (doKeyDown (wParam)) return 0; - case WM_ACTIVATE: - if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE) - { - handleAppActivation (wParam); - return 0; - } - - break; - - case WM_NCACTIVATE: - // while a temporary window is being shown, prevent Windows from deactivating the - // title bars of our main windows. - if (wParam == 0 && ! shouldDeactivateTitleBar) - wParam = TRUE; // change this and let it get passed to the DefWindowProc. - - break; - - case WM_MOUSEACTIVATE: - if (! component->getMouseClickGrabsKeyboardFocus()) - return MA_NOACTIVATE; - - break; - - case WM_SHOWWINDOW: - if (wParam != 0) - handleBroughtToFront(); - - break; - - case WM_CLOSE: - if (! component->isCurrentlyBlockedByAnotherModalComponent()) - handleUserClosingWindow(); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + if (doKeyUp (wParam)) return 0; - case WM_QUERYENDSESSION: - if (JUCEApplication::getInstance() != 0) - { - JUCEApplication::getInstance()->systemRequestedQuit(); - return MessageManager::getInstance()->hasStopMessageBeenSent(); - } + break; + + case WM_CHAR: + if (doKeyChar ((int) wParam, lParam)) + return 0; + + break; + + case WM_APPCOMMAND: + if (doAppCommand (lParam)) return TRUE; - case WM_TRAYNOTIFY: - handleTaskBarEvent (lParam, wParam); + break; + + //============================================================================== + case WM_SETFOCUS: + updateKeyModifiers(); + handleFocusGain(); + break; + + case WM_KILLFOCUS: + if (hasCreatedCaret) + { + hasCreatedCaret = false; + DestroyCaret(); + } + + handleFocusLoss(); + break; + + case WM_ACTIVATEAPP: + // Windows does weird things to process priority when you swap apps, + // so this forces an update when the app is brought to the front + if (wParam != FALSE) + juce_repeatLastProcessPriority(); + else + Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus + + juce_CheckCurrentlyFocusedTopLevelWindow(); + modifiersAtLastCallback = -1; + return 0; + + case WM_ACTIVATE: + if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE) + { + handleAppActivation (wParam); + return 0; + } + + break; + + case WM_NCACTIVATE: + // while a temporary window is being shown, prevent Windows from deactivating the + // title bars of our main windows. + if (wParam == 0 && ! shouldDeactivateTitleBar) + wParam = TRUE; // change this and let it get passed to the DefWindowProc. + + break; + + case WM_MOUSEACTIVATE: + if (! component->getMouseClickGrabsKeyboardFocus()) + return MA_NOACTIVATE; + + break; + + case WM_SHOWWINDOW: + if (wParam != 0) + handleBroughtToFront(); + + break; + + case WM_CLOSE: + if (! component->isCurrentlyBlockedByAnotherModalComponent()) + handleUserClosingWindow(); + + return 0; + + case WM_QUERYENDSESSION: + if (JUCEApplication::getInstance() != 0) + { + JUCEApplication::getInstance()->systemRequestedQuit(); + return MessageManager::getInstance()->hasStopMessageBeenSent(); + } + return TRUE; + + case WM_TRAYNOTIFY: + handleTaskBarEvent (lParam, wParam); + break; + + case WM_SYNCPAINT: + return 0; + + case WM_PALETTECHANGED: + InvalidateRect (h, 0, 0); + break; + + case WM_DISPLAYCHANGE: + InvalidateRect (h, 0, 0); + createPaletteIfNeeded = true; + // intentional fall-through... + case WM_SETTINGCHANGE: // note the fall-through in the previous case! + doSettingChange(); + break; + + case WM_INITMENU: + if (! hasTitleBar()) + { + if (isFullScreen()) + { + EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); + } + else if (! isMinimised()) + { + EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); + } + } + break; + + case WM_SYSCOMMAND: + switch (wParam & 0xfff0) + { + case SC_CLOSE: + if (sendInputAttemptWhenModalMessage()) + return 0; + + if (hasTitleBar()) + { + PostMessage (h, WM_CLOSE, 0, 0); + return 0; + } break; - case WM_SYNCPAINT: + case SC_KEYMENU: + // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure + // situations that can arise if a modal loop is started from an alt-key keypress). + if (hasTitleBar() && h == GetCapture()) + ReleaseCapture(); + + break; + + case SC_MAXIMIZE: + if (! sendInputAttemptWhenModalMessage()) + return 0; + + setFullScreen (true); return 0; - case WM_PALETTECHANGED: - InvalidateRect (h, 0, 0); - break; + case SC_MINIMIZE: + if (sendInputAttemptWhenModalMessage()) + return 0; - case WM_DISPLAYCHANGE: - InvalidateRect (h, 0, 0); - createPaletteIfNeeded = true; - // intentional fall-through... - case WM_SETTINGCHANGE: // note the fall-through in the previous case! - doSettingChange(); - break; - - case WM_INITMENU: if (! hasTitleBar()) + { + setMinimised (true); + return 0; + } + break; + + case SC_RESTORE: + if (sendInputAttemptWhenModalMessage()) + return 0; + + if (hasTitleBar()) { if (isFullScreen()) { - EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); - } - else if (! isMinimised()) - { - EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED); + setFullScreen (false); + return 0; } } - break; - - case WM_SYSCOMMAND: - switch (wParam & 0xfff0) + else { - case SC_CLOSE: - if (sendInputAttemptWhenModalMessage()) - return 0; + if (isMinimised()) + setMinimised (false); + else if (isFullScreen()) + setFullScreen (false); - if (hasTitleBar()) - { - PostMessage (h, WM_CLOSE, 0, 0); - return 0; - } - break; - - case SC_KEYMENU: - // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure - // situations that can arise if a modal loop is started from an alt-key keypress). - if (hasTitleBar() && h == GetCapture()) - ReleaseCapture(); - - break; - - case SC_MAXIMIZE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - setFullScreen (true); return 0; - - case SC_MINIMIZE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - if (! hasTitleBar()) - { - setMinimised (true); - return 0; - } - break; - - case SC_RESTORE: - if (sendInputAttemptWhenModalMessage()) - return 0; - - if (hasTitleBar()) - { - if (isFullScreen()) - { - setFullScreen (false); - return 0; - } - } - else - { - if (isMinimised()) - setMinimised (false); - else if (isFullScreen()) - setFullScreen (false); - - return 0; - } - - break; } - break; + } - case WM_NCLBUTTONDOWN: - case WM_NCRBUTTONDOWN: - case WM_NCMBUTTONDOWN: - sendInputAttemptWhenModalMessage(); - break; + break; - //case WM_IME_STARTCOMPOSITION; - // return 0; + case WM_NCLBUTTONDOWN: + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN: + sendInputAttemptWhenModalMessage(); + break; - case WM_GETDLGCODE: - return DLGC_WANTALLKEYS; + //case WM_IME_STARTCOMPOSITION; + // return 0; - default: - if (taskBarIcon != 0) + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + default: + if (taskBarIcon != 0) + { + static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated")); + + if (message == taskbarCreatedMessage) { - static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated")); - - if (message == taskbarCreatedMessage) - { - taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; - Shell_NotifyIcon (NIM_ADD, taskBarIcon); - } + taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + Shell_NotifyIcon (NIM_ADD, taskBarIcon); } + } - break; - } + break; } return DefWindowProcW (h, message, wParam, lParam);