Loading...
Searching...
No Matches
juce_URL.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct FallbackDownloadTask final : public URL::DownloadTask,
public Thread
{
FallbackDownloadTask (std::unique_ptr<FileOutputStream> outputStreamToUse,
size_t bufferSizeToUse,
std::unique_ptr<WebInputStream> streamToUse,
URL::DownloadTask::Listener* listenerToUse)
: Thread ("DownloadTask thread"),
fileStream (std::move (outputStreamToUse)),
stream (std::move (streamToUse)),
bufferSize (bufferSizeToUse),
buffer (bufferSize),
listener (listenerToUse)
{
jassert (fileStream != nullptr);
jassert (stream != nullptr);
targetLocation = fileStream->getFile();
contentLength = stream->getTotalLength();
httpCode = stream->getStatusCode();
startThread();
}
~FallbackDownloadTask() override
{
signalThreadShouldExit();
stream->cancel();
waitForThreadToExit (-1);
}
//==============================================================================
void run() override
{
while (! (stream->isExhausted() || stream->isError() || threadShouldExit()))
{
if (listener != nullptr)
listener->progress (this, downloaded, contentLength);
auto max = (int) jmin ((int64) bufferSize, contentLength < 0 ? std::numeric_limits<int64>::max()
: static_cast<int64> (contentLength - downloaded));
auto actual = stream->read (buffer.get(), max);
if (actual < 0 || threadShouldExit() || stream->isError())
break;
if (! fileStream->write (buffer.get(), static_cast<size_t> (actual)))
{
error = true;
break;
}
downloaded += actual;
if (downloaded == contentLength)
break;
}
fileStream.reset();
if (threadShouldExit() || stream->isError())
error = true;
if (contentLength > 0 && downloaded < contentLength)
error = true;
finished = true;
if (listener != nullptr && ! threadShouldExit())
listener->finished (this, ! error);
}
//==============================================================================
std::unique_ptr<FileOutputStream> fileStream;
const std::unique_ptr<WebInputStream> stream;
const size_t bufferSize;
HeapBlock<char> buffer;
URL::DownloadTask::Listener* const listener;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FallbackDownloadTask)
};
void URL::DownloadTaskListener::progress (DownloadTask*, int64, int64) {}
//==============================================================================
std::unique_ptr<URL::DownloadTask> URL::DownloadTask::createFallbackDownloader (const URL& urlToUse,
const File& targetFileToUse,
const DownloadTaskOptions& options)
{
const size_t bufferSize = 0x8000;
targetFileToUse.deleteFile();
if (auto outputStream = targetFileToUse.createOutputStream (bufferSize))
{
auto stream = std::make_unique<WebInputStream> (urlToUse, options.usePost);
stream->withExtraHeaders (options.extraHeaders);
if (stream->connect (nullptr))
return std::make_unique<FallbackDownloadTask> (std::move (outputStream),
bufferSize,
std::move (stream),
options.listener);
}
return nullptr;
}
URL::DownloadTask::DownloadTask() {}
URL::DownloadTask::~DownloadTask() {}
//==============================================================================
URL::URL() {}
URL::URL (const String& u) : url (u)
{
init();
}
URL::URL (File localFile)
{
if (localFile == File())
return;
#if JUCE_WINDOWS
bool isUncPath = localFile.getFullPathName().startsWith ("\\\\");
#endif
while (! localFile.isRoot())
{
url = "/" + addEscapeChars (localFile.getFileName(), false) + url;
localFile = localFile.getParentDirectory();
}
url = addEscapeChars (localFile.getFileName(), false) + url;
#if JUCE_WINDOWS
if (isUncPath)
{
url = url.fromFirstOccurrenceOf ("/", false, false);
}
else
#endif
{
if (! url.startsWithChar (L'/'))
url = "/" + url;
}
url = "file://" + url;
jassert (isWellFormed());
}
void URL::init()
{
auto i = url.indexOfChar ('#');
if (i >= 0)
{
anchor = removeEscapeChars (url.substring (i + 1));
url = url.upToFirstOccurrenceOf ("#", false, false);
}
i = url.indexOfChar ('?');
if (i >= 0)
{
do
{
auto nextAmp = url.indexOfChar (i + 1, '&');
auto equalsPos = url.indexOfChar (i + 1, '=');
if (nextAmp < 0)
{
addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1) : url.substring (i + 1, equalsPos)),
equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1)));
}
else if (nextAmp > 0 && equalsPos < nextAmp)
{
addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1, nextAmp) : url.substring (i + 1, equalsPos)),
equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
}
i = nextAmp;
}
while (i >= 0);
url = url.upToFirstOccurrenceOf ("?", false, false);
}
}
URL::URL (const String& u, int) : url (u) {}
URL URL::createWithoutParsing (const String& u)
{
return URL (u, 0);
}
bool URL::operator== (const URL& other) const
{
return url == other.url
&& postData == other.postData
&& parameterNames == other.parameterNames
&& parameterValues == other.parameterValues
&& filesToUpload == other.filesToUpload;
}
bool URL::operator!= (const URL& other) const
{
return ! operator== (other);
}
namespace URLHelpers
{
static String getMangledParameters (const URL& url)
{
jassert (url.getParameterNames().size() == url.getParameterValues().size());
String p;
for (int i = 0; i < url.getParameterNames().size(); ++i)
{
if (i > 0)
p << '&';
auto val = url.getParameterValues()[i];
p << URL::addEscapeChars (url.getParameterNames()[i], true);
if (val.isNotEmpty())
p << '=' << URL::addEscapeChars (val, true);
}
return p;
}
static int findEndOfScheme (const String& url)
{
int i = 0;
while (CharacterFunctions::isLetterOrDigit (url[i])
|| url[i] == '+' || url[i] == '-' || url[i] == '.')
++i;
return url.substring (i).startsWith ("://") ? i + 1 : 0;
}
static int findStartOfNetLocation (const String& url)
{
int start = findEndOfScheme (url);
while (url[start] == '/')
++start;
return start;
}
static int findStartOfPath (const String& url)
{
return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
}
static void concatenatePaths (String& path, const String& suffix)
{
if (! path.endsWithChar ('/'))
path << '/';
if (suffix.startsWithChar ('/'))
path += suffix.substring (1);
else
path += suffix;
}
static String removeLastPathSection (const String& url)
{
auto startOfPath = findStartOfPath (url);
auto lastSlash = url.lastIndexOfChar ('/');
if (lastSlash > startOfPath && lastSlash == url.length() - 1)
return removeLastPathSection (url.dropLastCharacters (1));
if (lastSlash < 0)
return url;
return url.substring (0, std::max (startOfPath, lastSlash));
}
}
void URL::addParameter (const String& name, const String& value)
{
parameterNames.add (name);
parameterValues.add (value);
}
String URL::toString (bool includeGetParameters) const
{
if (includeGetParameters)
return url + getQueryString();
return url;
}
bool URL::isEmpty() const noexcept
{
return url.isEmpty();
}
bool URL::isWellFormed() const
{
//xxx TODO
return url.isNotEmpty();
}
String URL::getDomain() const
{
return getDomainInternal (false);
}
String URL::getSubPath (bool includeGetParameters) const
{
auto startOfPath = URLHelpers::findStartOfPath (url);
auto subPath = startOfPath <= 0 ? String()
: url.substring (startOfPath);
if (includeGetParameters)
subPath += getQueryString();
return subPath;
}
String URL::getQueryString() const
{
String result;
if (parameterNames.size() > 0)
result += "?" + URLHelpers::getMangledParameters (*this);
if (anchor.isNotEmpty())
result += getAnchorString();
return result;
}
String URL::getAnchorString() const
{
if (anchor.isNotEmpty())
return "#" + URL::addEscapeChars (anchor, true);
return {};
}
String URL::getScheme() const
{
return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
}
#if ! JUCE_ANDROID
bool URL::isLocalFile() const
{
return getScheme() == "file";
}
File URL::getLocalFile() const
{
return fileFromFileSchemeURL (*this);
}
String URL::getFileName() const
{
return toString (false).fromLastOccurrenceOf ("/", false, true);
}
#endif
URL::ParameterHandling URL::toHandling (bool usePostData)
{
return usePostData ? ParameterHandling::inPostData : ParameterHandling::inAddress;
}
File URL::fileFromFileSchemeURL (const URL& fileURL)
{
if (! fileURL.isLocalFile())
{
jassertfalse;
return {};
}
auto path = removeEscapeChars (fileURL.getDomainInternal (true)).replace ("+", "%2B");
#if JUCE_WINDOWS
bool isUncPath = (! fileURL.url.startsWith ("file:///"));
#else
path = File::getSeparatorString() + path;
#endif
auto urlElements = StringArray::fromTokens (fileURL.getSubPath(), "/", "");
for (auto urlElement : urlElements)
path += File::getSeparatorString() + removeEscapeChars (urlElement.replace ("+", "%2B"));
#if JUCE_WINDOWS
if (isUncPath)
path = "\\\\" + path;
#endif
return path;
}
int URL::getPort() const
{
auto colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
}
URL URL::withNewDomainAndPath (const String& newURL) const
{
URL u (*this);
u.url = newURL;
return u;
}
URL URL::withNewSubPath (const String& newPath) const
{
URL u (*this);
auto startOfPath = URLHelpers::findStartOfPath (url);
if (startOfPath > 0)
u.url = url.substring (0, startOfPath);
URLHelpers::concatenatePaths (u.url, newPath);
return u;
}
URL URL::getParentURL() const
{
URL u (*this);
u.url = URLHelpers::removeLastPathSection (u.url);
return u;
}
URL URL::getChildURL (const String& subPath) const
{
URL u (*this);
URLHelpers::concatenatePaths (u.url, subPath);
return u;
}
bool URL::hasBodyDataToSend() const
{
return filesToUpload.size() > 0 || ! postData.isEmpty();
}
void URL::createHeadersAndPostData (String& headers,
MemoryBlock& postDataToWrite,
bool addParametersToBody) const
{
MemoryOutputStream data (postDataToWrite, false);
if (filesToUpload.size() > 0)
{
// (this doesn't currently support mixing custom post-data with uploads..)
jassert (postData.isEmpty());
auto boundary = String::toHexString (Random::getSystemRandom().nextInt64());
headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
data << "--" << boundary;
for (int i = 0; i < parameterNames.size(); ++i)
{
data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i]
<< "\"\r\n\r\n" << parameterValues[i]
<< "\r\n--" << boundary;
}
for (auto* f : filesToUpload)
{
data << "\r\nContent-Disposition: form-data; name=\"" << f->parameterName
<< "\"; filename=\"" << f->filename << "\"\r\n";
if (f->mimeType.isNotEmpty())
data << "Content-Type: " << f->mimeType << "\r\n";
data << "Content-Transfer-Encoding: binary\r\n\r\n";
if (f->data != nullptr)
data << *f->data;
else
data << f->file;
data << "\r\n--" << boundary;
}
data << "--\r\n";
}
else
{
if (addParametersToBody)
data << URLHelpers::getMangledParameters (*this);
data << postData;
// if the user-supplied headers didn't contain a content-type, add one now..
if (! headers.containsIgnoreCase ("Content-Type"))
headers << "Content-Type: application/x-www-form-urlencoded\r\n";
headers << "Content-length: " << (int) data.getDataSize() << "\r\n";
}
}
//==============================================================================
bool URL::isProbablyAWebsiteURL (const String& possibleURL)
{
for (auto* protocol : { "http:", "https:", "ftp:" })
if (possibleURL.startsWithIgnoreCase (protocol))
return true;
if (possibleURL.containsChar ('@') || possibleURL.containsChar (' '))
return false;
auto topLevelDomain = possibleURL.upToFirstOccurrenceOf ("/", false, false)
.fromLastOccurrenceOf (".", false, false);
return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
}
bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
{
auto atSign = possibleEmailAddress.indexOfChar ('@');
return atSign > 0
&& possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
&& ! possibleEmailAddress.endsWithChar ('.');
}
String URL::getDomainInternal (bool ignorePort) const
{
auto start = URLHelpers::findStartOfNetLocation (url);
auto end1 = url.indexOfChar (start, '/');
auto end2 = ignorePort ? -1 : url.indexOfChar (start, ':');
auto end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
: ((end1 < 0 || end2 < 0) ? jmax (end1, end2)
: jmin (end1, end2));
return url.substring (start, end);
}
#if JUCE_IOS
URL::Bookmark::Bookmark (void* bookmarkToUse) : data (bookmarkToUse)
{
}
URL::Bookmark::~Bookmark()
{
[(NSData*) data release];
}
void setURLBookmark (URL& u, void* bookmark)
{
u.bookmark = new URL::Bookmark (bookmark);
}
void* getURLBookmark (URL& u)
{
if (u.bookmark.get() == nullptr)
return nullptr;
return u.bookmark.get()->data;
}
template <typename Stream> struct iOSFileStreamWrapperFlush { static void flush (Stream*) {} };
template <> struct iOSFileStreamWrapperFlush<FileOutputStream> { static void flush (OutputStream* o) { o->flush(); } };
template <typename Stream>
class iOSFileStreamWrapper final : public Stream
{
public:
iOSFileStreamWrapper (URL& urlToUse)
: Stream (getLocalFileAccess (urlToUse)),
url (urlToUse)
{}
~iOSFileStreamWrapper()
{
iOSFileStreamWrapperFlush<Stream>::flush (this);
if (NSData* bookmark = (NSData*) getURLBookmark (url))
{
BOOL isBookmarkStale = false;
NSError* error = nil;
auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
options: 0
relativeToURL: nil
bookmarkDataIsStale: &isBookmarkStale
error: &error];
if (error == nil)
{
if (isBookmarkStale)
updateStaleBookmark (nsURL, url);
[nsURL stopAccessingSecurityScopedResource];
}
else
{
[[maybe_unused]] auto desc = [error localizedDescription];
jassertfalse;
}
}
}
private:
URL url;
bool securityAccessSucceeded = false;
File getLocalFileAccess (URL& urlToUse)
{
if (NSData* bookmark = (NSData*) getURLBookmark (urlToUse))
{
BOOL isBookmarkStale = false;
NSError* error = nil;
auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
options: 0
relativeToURL: nil
bookmarkDataIsStale: &isBookmarkStale
error: &error];
if (error == nil)
{
securityAccessSucceeded = [nsURL startAccessingSecurityScopedResource];
if (isBookmarkStale)
updateStaleBookmark (nsURL, urlToUse);
return urlToUse.getLocalFile();
}
[[maybe_unused]] auto desc = [error localizedDescription];
jassertfalse;
}
return urlToUse.getLocalFile();
}
void updateStaleBookmark (NSURL* nsURL, URL& juceUrl)
{
NSError* error = nil;
NSData* bookmark = [nsURL bookmarkDataWithOptions: NSURLBookmarkCreationSuitableForBookmarkFile
includingResourceValuesForKeys: nil
relativeToURL: nil
error: &error];
if (error == nil)
setURLBookmark (juceUrl, (void*) bookmark);
else
jassertfalse;
}
};
#endif
//==============================================================================
template <typename Member, typename Item>
static URL::InputStreamOptions with (URL::InputStreamOptions options, Member&& member, Item&& item)
{
options.*member = std::forward<Item> (item);
return options;
}
URL::InputStreamOptions::InputStreamOptions (ParameterHandling handling) : parameterHandling (handling) {}
URL::InputStreamOptions URL::InputStreamOptions::withProgressCallback (std::function<bool (int, int)> cb) const
{
return with (*this, &InputStreamOptions::progressCallback, std::move (cb));
}
URL::InputStreamOptions URL::InputStreamOptions::withExtraHeaders (const String& headers) const
{
return with (*this, &InputStreamOptions::extraHeaders, headers);
}
URL::InputStreamOptions URL::InputStreamOptions::withConnectionTimeoutMs (int timeout) const
{
return with (*this, &InputStreamOptions::connectionTimeOutMs, timeout);
}
URL::InputStreamOptions URL::InputStreamOptions::withResponseHeaders (StringPairArray* headers) const
{
return with (*this, &InputStreamOptions::responseHeaders, headers);
}
URL::InputStreamOptions URL::InputStreamOptions::withStatusCode (int* status) const
{
return with (*this, &InputStreamOptions::statusCode, status);
}
URL::InputStreamOptions URL::InputStreamOptions::withNumRedirectsToFollow (int numRedirects) const
{
return with (*this, &InputStreamOptions::numRedirectsToFollow, numRedirects);
}
URL::InputStreamOptions URL::InputStreamOptions::withHttpRequestCmd (const String& cmd) const
{
return with (*this, &InputStreamOptions::httpRequestCmd, cmd);
}
//==============================================================================
std::unique_ptr<InputStream> URL::createInputStream (const InputStreamOptions& options) const
{
if (isLocalFile())
{
#if JUCE_IOS
// We may need to refresh the embedded bookmark.
return std::make_unique<iOSFileStreamWrapper<FileInputStream>> (const_cast<URL&> (*this));
#else
return getLocalFile().createInputStream();
#endif
}
auto webInputStream = [&]
{
const auto usePost = options.getParameterHandling() == ParameterHandling::inPostData;
auto stream = std::make_unique<WebInputStream> (*this, usePost);
auto extraHeaders = options.getExtraHeaders();
if (extraHeaders.isNotEmpty())
stream->withExtraHeaders (extraHeaders);
auto timeout = options.getConnectionTimeoutMs();
if (timeout != 0)
stream->withConnectionTimeout (timeout);
auto requestCmd = options.getHttpRequestCmd();
if (requestCmd.isNotEmpty())
stream->withCustomRequestCommand (requestCmd);
stream->withNumRedirectsToFollow (options.getNumRedirectsToFollow());
return stream;
}();
struct ProgressCallbackCaller final : public WebInputStream::Listener
{
ProgressCallbackCaller (std::function<bool (int, int)> progressCallbackToUse)
: callback (std::move (progressCallbackToUse))
{
}
bool postDataSendProgress (WebInputStream&, int bytesSent, int totalBytes) override
{
return callback (bytesSent, totalBytes);
}
std::function<bool (int, int)> callback;
};
auto callbackCaller = [&options]() -> std::unique_ptr<ProgressCallbackCaller>
{
if (auto progressCallback = options.getProgressCallback())
return std::make_unique<ProgressCallbackCaller> (progressCallback);
return {};
}();
auto success = webInputStream->connect (callbackCaller.get());
if (auto* status = options.getStatusCode())
*status = webInputStream->getStatusCode();
if (auto* responseHeaders = options.getResponseHeaders())
*responseHeaders = webInputStream->getResponseHeaders();
if (! success || webInputStream->isError())
return nullptr;
// std::move() needed here for older compilers
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
return std::move (webInputStream);
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}
std::unique_ptr<OutputStream> URL::createOutputStream() const
{
#if JUCE_ANDROID
if (auto stream = AndroidDocument::fromDocument (*this).createOutputStream())
return stream;
#endif
if (isLocalFile())
{
#if JUCE_IOS
// We may need to refresh the embedded bookmark.
return std::make_unique<iOSFileStreamWrapper<FileOutputStream>> (const_cast<URL&> (*this));
#else
return std::make_unique<FileOutputStream> (getLocalFile());
#endif
}
return nullptr;
}
//==============================================================================
bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const
{
const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
: createInputStream (InputStreamOptions (toHandling (usePostCommand))));
if (in != nullptr)
{
in->readIntoMemoryBlock (destData);
return true;
}
return false;
}
String URL::readEntireTextStream (bool usePostCommand) const
{
const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
: createInputStream (InputStreamOptions (toHandling (usePostCommand))));
if (in != nullptr)
return in->readEntireStreamAsString();
return {};
}
std::unique_ptr<XmlElement> URL::readEntireXmlStream (bool usePostCommand) const
{
return parseXML (readEntireTextStream (usePostCommand));
}
//==============================================================================
URL URL::withParameter (const String& parameterName,
const String& parameterValue) const
{
auto u = *this;
u.addParameter (parameterName, parameterValue);
return u;
}
URL URL::withParameters (const StringPairArray& parametersToAdd) const
{
auto u = *this;
for (int i = 0; i < parametersToAdd.size(); ++i)
u.addParameter (parametersToAdd.getAllKeys()[i],
parametersToAdd.getAllValues()[i]);
return u;
}
URL URL::withAnchor (const String& anchorToAdd) const
{
auto u = *this;
u.anchor = anchorToAdd;
return u;
}
URL URL::withPOSTData (const String& newPostData) const
{
return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8()));
}
URL URL::withPOSTData (const MemoryBlock& newPostData) const
{
auto u = *this;
u.postData = newPostData;
return u;
}
URL::Upload::Upload (const String& param, const String& name,
const String& mime, const File& f, MemoryBlock* mb)
: parameterName (param), filename (name), mimeType (mime), file (f), data (mb)
{
jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
}
URL URL::withUpload (Upload* const f) const
{
auto u = *this;
for (int i = u.filesToUpload.size(); --i >= 0;)
if (u.filesToUpload.getObjectPointerUnchecked (i)->parameterName == f->parameterName)
u.filesToUpload.remove (i);
u.filesToUpload.add (f);
return u;
}
URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload,
const String& mimeType) const
{
return withUpload (new Upload (parameterName, fileToUpload.getFileName(),
mimeType, fileToUpload, nullptr));
}
URL URL::withDataToUpload (const String& parameterName, const String& filename,
const MemoryBlock& fileContentToUpload, const String& mimeType) const
{
return withUpload (new Upload (parameterName, filename, mimeType, File(),
new MemoryBlock (fileContentToUpload)));
}
//==============================================================================
String URL::removeEscapeChars (const String& s)
{
auto result = s.replaceCharacter ('+', ' ');
if (! result.containsChar ('%'))
return result;
// We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
// after all the replacements have been made, so that multi-byte chars are handled.
Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
for (int i = 0; i < utf8.size(); ++i)
{
if (utf8.getUnchecked (i) == '%')
{
auto hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]);
auto hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]);
if (hexDigit1 >= 0 && hexDigit2 >= 0)
{
utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
utf8.removeRange (i + 1, 2);
}
}
}
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
}
String URL::addEscapeChars (const String& s, bool isParameter, bool roundBracketsAreLegal)
{
String legalChars (isParameter ? "_-.~"
: ",$_-.*!'");
if (roundBracketsAreLegal)
legalChars += "()";
Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
for (int i = 0; i < utf8.size(); ++i)
{
auto c = utf8.getUnchecked (i);
if (! (CharacterFunctions::isLetterOrDigit (c)
|| legalChars.containsChar ((juce_wchar) c)))
{
utf8.set (i, '%');
utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]);
utf8.insert (++i, "0123456789ABCDEF" [c & 15]);
}
}
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
}
//==============================================================================
bool URL::launchInDefaultBrowser() const
{
auto u = toString (true);
if (u.containsChar ('@') && ! u.containsChar (':'))
u = "mailto:" + u;
return Process::openDocument (u, {});
}
//==============================================================================
std::unique_ptr<InputStream> URL::createInputStream (bool usePostCommand,
OpenStreamProgressCallback* cb,
void* context,
String headers,
int timeOutMs,
StringPairArray* responseHeaders,
int* statusCode,
int numRedirectsToFollow,
String httpRequestCmd) const
{
std::function<bool (int, int)> callback;
if (cb != nullptr)
callback = [context, cb] (int sent, int total) { return cb (context, sent, total); };
return createInputStream (InputStreamOptions (toHandling (usePostCommand))
.withProgressCallback (std::move (callback))
.withExtraHeaders (headers)
.withConnectionTimeoutMs (timeOutMs)
.withResponseHeaders (responseHeaders)
.withStatusCode (statusCode)
.withNumRedirectsToFollow (numRedirectsToFollow)
.withHttpRequestCmd (httpRequestCmd));
}
std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation,
String extraHeaders,
DownloadTask::Listener* listener,
bool usePostCommand)
{
auto options = DownloadTaskOptions().withExtraHeaders (std::move (extraHeaders))
.withListener (listener)
.withUsePost (usePostCommand);
return downloadToFile (targetLocation, std::move (options));
}
} // namespace juce
juce::AndroidDocument::fromDocument
static AndroidDocument fromDocument(const URL &documentUrl)
juce::Array
Definition juce_Array.h:56
juce::Array::getUnchecked
ElementType getUnchecked(int index) const
Definition juce_Array.h:252
juce::Array::size
int size() const noexcept
Definition juce_Array.h:215
juce::Array::removeRange
void removeRange(int startIndex, int numberToRemove)
Definition juce_Array.h:894
juce::Array::insert
void insert(int indexToInsertAt, ParameterType newElement)
Definition juce_Array.h:462
juce::Array::getRawDataPointer
ElementType * getRawDataPointer() noexcept
Definition juce_Array.h:310
juce::Array::set
void set(int indexToChange, ParameterType newValue)
Definition juce_Array.h:542
juce::CharacterFunctions::getHexDigitValue
static int getHexDigitValue(juce_wchar digit) noexcept
Definition juce_CharacterFunctions.cpp:112
juce::CharacterFunctions::isLetterOrDigit
static bool isLetterOrDigit(char character) noexcept
Definition juce_CharacterFunctions.cpp:90
juce::FileOutputStream
Definition juce_FileOutputStream.h:35
juce::FileOutputStream::flush
void flush() override
Definition juce_FileOutputStream.cpp:70
juce::File
Definition juce_File.h:45
juce::File::createOutputStream
std::unique_ptr< FileOutputStream > createOutputStream(size_t bufferSize=0x8000) const
Definition juce_File.cpp:742
juce::File::getFullPathName
const String & getFullPathName() const noexcept
Definition juce_File.h:153
juce::File::getFileName
String getFileName() const
Definition juce_File.cpp:372
juce::File::isRoot
bool isRoot() const
Definition juce_File.cpp:125
juce::File::getParentDirectory
File getParentDirectory() const
Definition juce_File.cpp:358
juce::File::createInputStream
std::unique_ptr< FileInputStream > createInputStream() const
Definition juce_File.cpp:732
juce::File::deleteFile
bool deleteFile() const
juce::File::getSeparatorString
static StringRef getSeparatorString()
juce::MemoryBlock
Definition juce_MemoryBlock.h:33
juce::MemoryBlock::isEmpty
bool isEmpty() const noexcept
Definition juce_MemoryBlock.h:124
juce::Process::openDocument
static bool JUCE_CALLTYPE openDocument(const String &documentURL, const String &parameters)
juce::Random::getSystemRandom
static Random & getSystemRandom() noexcept
Definition juce_Random.cpp:67
juce::ReferenceCountedArray::size
int size() const noexcept
Definition juce_ReferenceCountedArray.h:150
juce::StringArray::fromTokens
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Definition juce_StringArray.cpp:387
juce::StringArray::size
int size() const noexcept
Definition juce_StringArray.h:136
juce::StringPairArray
Definition juce_StringPairArray.h:35
juce::StringPairArray::getAllValues
const StringArray & getAllValues() const noexcept
Definition juce_StringPairArray.h:90
juce::StringPairArray::size
int size() const noexcept
Definition juce_StringPairArray.h:93
juce::StringPairArray::getAllKeys
const StringArray & getAllKeys() const noexcept
Definition juce_StringPairArray.h:87
juce::String
Definition juce_String.h:53
juce::String::indexOfChar
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Definition juce_String.cpp:874
juce::String::upToFirstOccurrenceOf
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1585
juce::String::endsWithChar
bool endsWithChar(juce_wchar character) const noexcept
Definition juce_String.cpp:1410
juce::String::toRawUTF8
const char * toRawUTF8() const
Definition juce_String.cpp:2074
juce::String::startsWith
bool startsWith(StringRef text) const noexcept
Definition juce_String.cpp:1393
juce::String::containsChar
bool containsChar(juce_wchar character) const noexcept
Definition juce_String.cpp:1040
juce::String::startsWithIgnoreCase
bool startsWithIgnoreCase(StringRef text) const noexcept
Definition juce_String.cpp:1398
juce::String::getNumBytesAsUTF8
size_t getNumBytesAsUTF8() const noexcept
Definition juce_String.cpp:2120
juce::String::toHexString
static String toHexString(IntegerType number)
Definition juce_String.h:1097
juce::String::lastIndexOfChar
int lastIndexOfChar(juce_wchar character) const noexcept
Definition juce_String.cpp:899
juce::String::replace
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
Definition juce_String.cpp:1279
juce::String::replaceCharacter
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
Definition juce_String.cpp:1344
juce::String::substring
String substring(int startIndex, int endIndex) const
Definition juce_String.cpp:1498
juce::String::fromLastOccurrenceOf
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1575
juce::String::fromUTF8
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Definition juce_String.cpp:2125
juce::String::isNotEmpty
bool isNotEmpty() const noexcept
Definition juce_String.h:316
juce::Thread
Definition juce_Thread.h:43
juce::Thread::waitForThreadToExit
bool waitForThreadToExit(int timeOutMilliseconds) const
Definition juce_Thread.cpp:215
juce::Thread::Thread
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
Definition juce_Thread.cpp:26
juce::Thread::run
virtual void run()=0
juce::Thread::startThread
bool startThread()
Definition juce_Thread.cpp:145
juce::Thread::threadShouldExit
bool threadShouldExit() const
Definition juce_Thread.cpp:202
juce::Thread::signalThreadShouldExit
void signalThreadShouldExit()
Definition juce_Thread.cpp:196
juce::URL::DownloadTaskOptions
Definition juce_URL.h:460
juce::URL::DownloadTaskOptions::withExtraHeaders
auto withExtraHeaders(String value) const
Definition juce_URL.h:468
juce::URL::DownloadTask
Definition juce_URL.h:500
juce::URL::DownloadTask::~DownloadTask
virtual ~DownloadTask()
Definition juce_URL.cpp:135
juce::URL::InputStreamOptions
Definition juce_URL.h:331
juce::URL::InputStreamOptions::withExtraHeaders
InputStreamOptions withExtraHeaders(const String &extraHeaders) const
Definition juce_URL.cpp:704
juce::URL::InputStreamOptions::withResponseHeaders
InputStreamOptions withResponseHeaders(StringPairArray *responseHeaders) const
Definition juce_URL.cpp:714
juce::URL::InputStreamOptions::withStatusCode
InputStreamOptions withStatusCode(int *statusCode) const
Definition juce_URL.cpp:719
juce::URL::InputStreamOptions::withNumRedirectsToFollow
InputStreamOptions withNumRedirectsToFollow(int numRedirectsToFollow) const
Definition juce_URL.cpp:724
juce::URL::InputStreamOptions::withConnectionTimeoutMs
InputStreamOptions withConnectionTimeoutMs(int connectionTimeoutMs) const
Definition juce_URL.cpp:709
juce::URL::InputStreamOptions::InputStreamOptions
InputStreamOptions(ParameterHandling parameterHandling)
Definition juce_URL.cpp:697
juce::URL::InputStreamOptions::withProgressCallback
InputStreamOptions withProgressCallback(std::function< bool(int bytesSent, int totalBytes)> progressCallback) const
Definition juce_URL.cpp:699
juce::URL::InputStreamOptions::withHttpRequestCmd
InputStreamOptions withHttpRequestCmd(const String &httpRequestCmd) const
Definition juce_URL.cpp:729
juce::URL
Definition juce_URL.h:38
juce::URL::withParameter
URL withParameter(const String &parameterName, const String &parameterValue) const
Definition juce_URL.cpp:864
juce::URL::createWithoutParsing
static URL createWithoutParsing(const String &url)
Definition juce_URL.cpp:219
juce::URL::getLocalFile
File getLocalFile() const
Definition juce_URL.cpp:387
juce::URL::isWellFormed
bool isWellFormed() const
Definition juce_URL.cpp:332
juce::URL::readEntireBinaryStream
bool readEntireBinaryStream(MemoryBlock &destData, bool usePostCommand=false) const
Definition juce_URL.cpp:833
juce::URL::getPort
int getPort() const
Definition juce_URL.cpp:432
juce::URL::withDataToUpload
URL withDataToUpload(const String &parameterName, const String &filename, const MemoryBlock &fileContentToUpload, const String &mimeType) const
Definition juce_URL.cpp:929
juce::URL::getFileName
String getFileName() const
Definition juce_URL.cpp:392
juce::URL::getChildURL
URL getChildURL(const String &subPath) const
Definition juce_URL.cpp:466
juce::URL::removeEscapeChars
static String removeEscapeChars(const String &stringToRemoveEscapeCharsFrom)
Definition juce_URL.cpp:937
juce::URL::getAnchorString
String getAnchorString() const
Definition juce_URL.cpp:368
juce::URL::addEscapeChars
static String addEscapeChars(const String &stringToAddEscapeCharsTo, bool isParameter, bool roundBracketsAreLegal=true)
Definition juce_URL.cpp:966
juce::URL::withAnchor
URL withAnchor(const String &anchor) const
Definition juce_URL.cpp:883
juce::URL::toString
String toString(bool includeGetParameters) const
Definition juce_URL.cpp:319
juce::URL::createOutputStream
std::unique_ptr< OutputStream > createOutputStream() const
Definition juce_URL.cpp:812
juce::URL::getSubPath
String getSubPath(bool includeGetParameters=false) const
Definition juce_URL.cpp:343
juce::URL::getParentURL
URL getParentURL() const
Definition juce_URL.cpp:459
juce::URL::getQueryString
String getQueryString() const
Definition juce_URL.cpp:355
juce::URL::withNewSubPath
URL withNewSubPath(const String &newPath) const
Definition juce_URL.cpp:446
juce::URL::isProbablyAnEmailAddress
static bool isProbablyAnEmailAddress(const String &possibleEmailAddress)
Definition juce_URL.cpp:553
juce::URL::withNewDomainAndPath
URL withNewDomainAndPath(const String &newFullPath) const
Definition juce_URL.cpp:439
juce::URL::readEntireTextStream
String readEntireTextStream(bool usePostCommand=false) const
Definition juce_URL.cpp:847
juce::URL::isEmpty
bool isEmpty() const noexcept
Definition juce_URL.cpp:327
juce::URL::isProbablyAWebsiteURL
static bool isProbablyAWebsiteURL(const String &possibleURL)
Definition juce_URL.cpp:538
juce::URL::createInputStream
std::unique_ptr< InputStream > createInputStream(const InputStreamOptions &options) const
Definition juce_URL.cpp:735
juce::URL::downloadToFile
std::unique_ptr< DownloadTask > downloadToFile(const File &targetLocation, String extraHeaders=String(), DownloadTaskListener *listener=nullptr, bool usePostCommand=false)
Definition juce_URL.cpp:1029
juce::URL::getDomain
String getDomain() const
Definition juce_URL.cpp:338
juce::URL::isLocalFile
bool isLocalFile() const
Definition juce_URL.cpp:382
juce::URL::withFileToUpload
URL withFileToUpload(const String &parameterName, const File &fileToUpload, const String &mimeType) const
Definition juce_URL.cpp:922
juce::URL::withPOSTData
URL withPOSTData(const String &postData) const
Definition juce_URL.cpp:891
juce::URL::withParameters
URL withParameters(const StringPairArray &parametersToAdd) const
Definition juce_URL.cpp:872
juce::URL::readEntireXmlStream
std::unique_ptr< XmlElement > readEntireXmlStream(bool usePostCommand=false) const
Definition juce_URL.cpp:858
juce::URL::getScheme
String getScheme() const
Definition juce_URL.cpp:376
juce::URL::operator==
bool operator==(const URL &) const
Definition juce_URL.cpp:224
juce::URL::launchInDefaultBrowser
bool launchInDefaultBrowser() const
Definition juce_URL.cpp:993
juce::URL::URL
URL()
Definition juce_URL.cpp:138
juce::WebInputStream::Listener
Definition juce_WebInputStream.h:97
juce::URL::DownloadTaskListener::progress
virtual void progress(DownloadTask *task, int64 bytesDownloaded, int64 totalLength)
Definition juce_URL.cpp:109