OpenShot Audio Library | OpenShotAudio: juce_URL.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_URL.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26struct FallbackDownloadTask final : public URL::DownloadTask,
27 public Thread
28{
29 FallbackDownloadTask (std::unique_ptr<FileOutputStream> outputStreamToUse,
30 size_t bufferSizeToUse,
31 std::unique_ptr<WebInputStream> streamToUse,
32 URL::DownloadTask::Listener* listenerToUse)
33 : Thread ("DownloadTask thread"),
34 fileStream (std::move (outputStreamToUse)),
35 stream (std::move (streamToUse)),
36 bufferSize (bufferSizeToUse),
37 buffer (bufferSize),
38 listener (listenerToUse)
39 {
40 jassert (fileStream != nullptr);
41 jassert (stream != nullptr);
42
43 targetLocation = fileStream->getFile();
44 contentLength = stream->getTotalLength();
45 httpCode = stream->getStatusCode();
46
47 startThread();
48 }
49
50 ~FallbackDownloadTask() override
51 {
52 signalThreadShouldExit();
53 stream->cancel();
54 waitForThreadToExit (-1);
55 }
56
57 //==============================================================================
58 void run() override
59 {
60 while (! (stream->isExhausted() || stream->isError() || threadShouldExit()))
61 {
62 if (listener != nullptr)
63 listener->progress (this, downloaded, contentLength);
64
65 auto max = (int) jmin ((int64) bufferSize, contentLength < 0 ? std::numeric_limits<int64>::max()
66 : static_cast<int64> (contentLength - downloaded));
67
68 auto actual = stream->read (buffer.get(), max);
69
70 if (actual < 0 || threadShouldExit() || stream->isError())
71 break;
72
73 if (! fileStream->write (buffer.get(), static_cast<size_t> (actual)))
74 {
75 error = true;
76 break;
77 }
78
79 downloaded += actual;
80
81 if (downloaded == contentLength)
82 break;
83 }
84
85 fileStream.reset();
86
87 if (threadShouldExit() || stream->isError())
88 error = true;
89
90 if (contentLength > 0 && downloaded < contentLength)
91 error = true;
92
93 finished = true;
94
95 if (listener != nullptr && ! threadShouldExit())
96 listener->finished (this, ! error);
97 }
98
99 //==============================================================================
100 std::unique_ptr<FileOutputStream> fileStream;
101 const std::unique_ptr<WebInputStream> stream;
102 const size_t bufferSize;
103 HeapBlock<char> buffer;
104 URL::DownloadTask::Listener* const listener;
105
106 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FallbackDownloadTask)
107};
108
109 void URL::DownloadTaskListener::progress (DownloadTask*, int64, int64) {}
110
111//==============================================================================
112std::unique_ptr<URL::DownloadTask> URL::DownloadTask::createFallbackDownloader (const URL& urlToUse,
113 const File& targetFileToUse,
114 const DownloadTaskOptions& options)
115{
116 const size_t bufferSize = 0x8000;
117 targetFileToUse.deleteFile();
118
119 if (auto outputStream = targetFileToUse.createOutputStream (bufferSize))
120 {
121 auto stream = std::make_unique<WebInputStream> (urlToUse, options.usePost);
122 stream->withExtraHeaders (options.extraHeaders);
123
124 if (stream->connect (nullptr))
125 return std::make_unique<FallbackDownloadTask> (std::move (outputStream),
126 bufferSize,
127 std::move (stream),
128 options.listener);
129 }
130
131 return nullptr;
132}
133
134URL::DownloadTask::DownloadTask() {}
135URL::DownloadTask::~DownloadTask() {}
136
137//==============================================================================
138URL::URL() {}
139
140 URL::URL (const String& u) : url (u)
141{
142 init();
143}
144
145 URL::URL (File localFile)
146{
147 if (localFile == File())
148 return;
149
150 #if JUCE_WINDOWS
151 bool isUncPath = localFile.getFullPathName().startsWith ("\\\\");
152 #endif
153
154 while (! localFile.isRoot())
155 {
156 url = "/" + addEscapeChars (localFile.getFileName(), false) + url;
157 localFile = localFile.getParentDirectory();
158 }
159
160 url = addEscapeChars (localFile.getFileName(), false) + url;
161
162 #if JUCE_WINDOWS
163 if (isUncPath)
164 {
165 url = url.fromFirstOccurrenceOf ("/", false, false);
166 }
167 else
168 #endif
169 {
170 if (! url.startsWithChar (L'/'))
171 url = "/" + url;
172 }
173
174 url = "file://" + url;
175
176 jassert (isWellFormed());
177}
178
179void URL::init()
180{
181 auto i = url.indexOfChar ('#');
182
183 if (i >= 0)
184 {
185 anchor = removeEscapeChars (url.substring (i + 1));
186 url = url.upToFirstOccurrenceOf ("#", false, false);
187 }
188
189 i = url.indexOfChar ('?');
190
191 if (i >= 0)
192 {
193 do
194 {
195 auto nextAmp = url.indexOfChar (i + 1, '&');
196 auto equalsPos = url.indexOfChar (i + 1, '=');
197
198 if (nextAmp < 0)
199 {
200 addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1) : url.substring (i + 1, equalsPos)),
201 equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1)));
202 }
203 else if (nextAmp > 0 && equalsPos < nextAmp)
204 {
205 addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1, nextAmp) : url.substring (i + 1, equalsPos)),
206 equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
207 }
208
209 i = nextAmp;
210 }
211 while (i >= 0);
212
213 url = url.upToFirstOccurrenceOf ("?", false, false);
214 }
215}
216
217 URL::URL (const String& u, int) : url (u) {}
218
219 URL URL::createWithoutParsing (const String& u)
220{
221 return URL (u, 0);
222}
223
224 bool URL::operator== (const URL& other) const
225{
226 return url == other.url
227 && postData == other.postData
228 && parameterNames == other.parameterNames
229 && parameterValues == other.parameterValues
230 && filesToUpload == other.filesToUpload;
231}
232
233bool URL::operator!= (const URL& other) const
234{
235 return ! operator== (other);
236}
237
238namespace URLHelpers
239{
240 static String getMangledParameters (const URL& url)
241 {
242 jassert (url.getParameterNames().size() == url.getParameterValues().size());
243 String p;
244
245 for (int i = 0; i < url.getParameterNames().size(); ++i)
246 {
247 if (i > 0)
248 p << '&';
249
250 auto val = url.getParameterValues()[i];
251
252 p << URL::addEscapeChars (url.getParameterNames()[i], true);
253
254 if (val.isNotEmpty())
255 p << '=' << URL::addEscapeChars (val, true);
256 }
257
258 return p;
259 }
260
261 static int findEndOfScheme (const String& url)
262 {
263 int i = 0;
264
265 while (CharacterFunctions::isLetterOrDigit (url[i])
266 || url[i] == '+' || url[i] == '-' || url[i] == '.')
267 ++i;
268
269 return url.substring (i).startsWith ("://") ? i + 1 : 0;
270 }
271
272 static int findStartOfNetLocation (const String& url)
273 {
274 int start = findEndOfScheme (url);
275
276 while (url[start] == '/')
277 ++start;
278
279 return start;
280 }
281
282 static int findStartOfPath (const String& url)
283 {
284 return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
285 }
286
287 static void concatenatePaths (String& path, const String& suffix)
288 {
289 if (! path.endsWithChar ('/'))
290 path << '/';
291
292 if (suffix.startsWithChar ('/'))
293 path += suffix.substring (1);
294 else
295 path += suffix;
296 }
297
298 static String removeLastPathSection (const String& url)
299 {
300 auto startOfPath = findStartOfPath (url);
301 auto lastSlash = url.lastIndexOfChar ('/');
302
303 if (lastSlash > startOfPath && lastSlash == url.length() - 1)
304 return removeLastPathSection (url.dropLastCharacters (1));
305
306 if (lastSlash < 0)
307 return url;
308
309 return url.substring (0, std::max (startOfPath, lastSlash));
310 }
311}
312
313void URL::addParameter (const String& name, const String& value)
314{
315 parameterNames.add (name);
316 parameterValues.add (value);
317}
318
319 String URL::toString (bool includeGetParameters) const
320{
321 if (includeGetParameters)
322 return url + getQueryString();
323
324 return url;
325}
326
327 bool URL::isEmpty() const noexcept
328{
329 return url.isEmpty();
330}
331
332 bool URL::isWellFormed() const
333{
334 //xxx TODO
335 return url.isNotEmpty();
336}
337
338 String URL::getDomain() const
339{
340 return getDomainInternal (false);
341}
342
343 String URL::getSubPath (bool includeGetParameters) const
344{
345 auto startOfPath = URLHelpers::findStartOfPath (url);
346 auto subPath = startOfPath <= 0 ? String()
347 : url.substring (startOfPath);
348
349 if (includeGetParameters)
350 subPath += getQueryString();
351
352 return subPath;
353}
354
355 String URL::getQueryString() const
356{
357 String result;
358
359 if (parameterNames.size() > 0)
360 result += "?" + URLHelpers::getMangledParameters (*this);
361
362 if (anchor.isNotEmpty())
363 result += getAnchorString();
364
365 return result;
366}
367
368 String URL::getAnchorString() const
369{
370 if (anchor.isNotEmpty())
371 return "#" + URL::addEscapeChars (anchor, true);
372
373 return {};
374}
375
376 String URL::getScheme() const
377{
378 return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
379}
380
381#if ! JUCE_ANDROID
382 bool URL::isLocalFile() const
383{
384 return getScheme() == "file";
385}
386
387 File URL::getLocalFile() const
388{
389 return fileFromFileSchemeURL (*this);
390}
391
392 String URL::getFileName() const
393{
394 return toString (false).fromLastOccurrenceOf ("/", false, true);
395}
396#endif
397
398URL::ParameterHandling URL::toHandling (bool usePostData)
399{
400 return usePostData ? ParameterHandling::inPostData : ParameterHandling::inAddress;
401}
402
403File URL::fileFromFileSchemeURL (const URL& fileURL)
404{
405 if (! fileURL.isLocalFile())
406 {
407 jassertfalse;
408 return {};
409 }
410
411 auto path = removeEscapeChars (fileURL.getDomainInternal (true)).replace ("+", "%2B");
412
413 #if JUCE_WINDOWS
414 bool isUncPath = (! fileURL.url.startsWith ("file:///"));
415 #else
416 path = File::getSeparatorString() + path;
417 #endif
418
419 auto urlElements = StringArray::fromTokens (fileURL.getSubPath(), "/", "");
420
421 for (auto urlElement : urlElements)
422 path += File::getSeparatorString() + removeEscapeChars (urlElement.replace ("+", "%2B"));
423
424 #if JUCE_WINDOWS
425 if (isUncPath)
426 path = "\\\\" + path;
427 #endif
428
429 return path;
430}
431
432 int URL::getPort() const
433{
434 auto colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
435
436 return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
437}
438
439 URL URL::withNewDomainAndPath (const String& newURL) const
440{
441 URL u (*this);
442 u.url = newURL;
443 return u;
444}
445
446 URL URL::withNewSubPath (const String& newPath) const
447{
448 URL u (*this);
449
450 auto startOfPath = URLHelpers::findStartOfPath (url);
451
452 if (startOfPath > 0)
453 u.url = url.substring (0, startOfPath);
454
455 URLHelpers::concatenatePaths (u.url, newPath);
456 return u;
457}
458
459 URL URL::getParentURL() const
460{
461 URL u (*this);
462 u.url = URLHelpers::removeLastPathSection (u.url);
463 return u;
464}
465
466 URL URL::getChildURL (const String& subPath) const
467{
468 URL u (*this);
469 URLHelpers::concatenatePaths (u.url, subPath);
470 return u;
471}
472
473bool URL::hasBodyDataToSend() const
474{
475 return filesToUpload.size() > 0 || ! postData.isEmpty();
476}
477
478void URL::createHeadersAndPostData (String& headers,
479 MemoryBlock& postDataToWrite,
480 bool addParametersToBody) const
481{
482 MemoryOutputStream data (postDataToWrite, false);
483
484 if (filesToUpload.size() > 0)
485 {
486 // (this doesn't currently support mixing custom post-data with uploads..)
487 jassert (postData.isEmpty());
488
489 auto boundary = String::toHexString (Random::getSystemRandom().nextInt64());
490
491 headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
492
493 data << "--" << boundary;
494
495 for (int i = 0; i < parameterNames.size(); ++i)
496 {
497 data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i]
498 << "\"\r\n\r\n" << parameterValues[i]
499 << "\r\n--" << boundary;
500 }
501
502 for (auto* f : filesToUpload)
503 {
504 data << "\r\nContent-Disposition: form-data; name=\"" << f->parameterName
505 << "\"; filename=\"" << f->filename << "\"\r\n";
506
507 if (f->mimeType.isNotEmpty())
508 data << "Content-Type: " << f->mimeType << "\r\n";
509
510 data << "Content-Transfer-Encoding: binary\r\n\r\n";
511
512 if (f->data != nullptr)
513 data << *f->data;
514 else
515 data << f->file;
516
517 data << "\r\n--" << boundary;
518 }
519
520 data << "--\r\n";
521 }
522 else
523 {
524 if (addParametersToBody)
525 data << URLHelpers::getMangledParameters (*this);
526
527 data << postData;
528
529 // if the user-supplied headers didn't contain a content-type, add one now..
530 if (! headers.containsIgnoreCase ("Content-Type"))
531 headers << "Content-Type: application/x-www-form-urlencoded\r\n";
532
533 headers << "Content-length: " << (int) data.getDataSize() << "\r\n";
534 }
535}
536
537//==============================================================================
538 bool URL::isProbablyAWebsiteURL (const String& possibleURL)
539{
540 for (auto* protocol : { "http:", "https:", "ftp:" })
541 if (possibleURL.startsWithIgnoreCase (protocol))
542 return true;
543
544 if (possibleURL.containsChar ('@') || possibleURL.containsChar (' '))
545 return false;
546
547 auto topLevelDomain = possibleURL.upToFirstOccurrenceOf ("/", false, false)
548 .fromLastOccurrenceOf (".", false, false);
549
550 return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
551}
552
553 bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
554{
555 auto atSign = possibleEmailAddress.indexOfChar ('@');
556
557 return atSign > 0
558 && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
559 && ! possibleEmailAddress.endsWithChar ('.');
560}
561
562 String URL::getDomainInternal (bool ignorePort) const
563{
564 auto start = URLHelpers::findStartOfNetLocation (url);
565 auto end1 = url.indexOfChar (start, '/');
566 auto end2 = ignorePort ? -1 : url.indexOfChar (start, ':');
567
568 auto end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
569 : ((end1 < 0 || end2 < 0) ? jmax (end1, end2)
570 : jmin (end1, end2));
571 return url.substring (start, end);
572}
573
574#if JUCE_IOS
575URL::Bookmark::Bookmark (void* bookmarkToUse) : data (bookmarkToUse)
576{
577}
578
579URL::Bookmark::~Bookmark()
580{
581 [(NSData*) data release];
582}
583
584void setURLBookmark (URL& u, void* bookmark)
585{
586 u.bookmark = new URL::Bookmark (bookmark);
587}
588
589void* getURLBookmark (URL& u)
590{
591 if (u.bookmark.get() == nullptr)
592 return nullptr;
593
594 return u.bookmark.get()->data;
595}
596
597template <typename Stream> struct iOSFileStreamWrapperFlush { static void flush (Stream*) {} };
598template <> struct iOSFileStreamWrapperFlush<FileOutputStream> { static void flush (OutputStream* o) { o->flush(); } };
599
600template <typename Stream>
601class iOSFileStreamWrapper final : public Stream
602{
603public:
604 iOSFileStreamWrapper (URL& urlToUse)
605 : Stream (getLocalFileAccess (urlToUse)),
606 url (urlToUse)
607 {}
608
609 ~iOSFileStreamWrapper()
610 {
611 iOSFileStreamWrapperFlush<Stream>::flush (this);
612
613 if (NSData* bookmark = (NSData*) getURLBookmark (url))
614 {
615 BOOL isBookmarkStale = false;
616 NSError* error = nil;
617
618 auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
619 options: 0
620 relativeToURL: nil
621 bookmarkDataIsStale: &isBookmarkStale
622 error: &error];
623
624 if (error == nil)
625 {
626 if (isBookmarkStale)
627 updateStaleBookmark (nsURL, url);
628
629 [nsURL stopAccessingSecurityScopedResource];
630 }
631 else
632 {
633 [[maybe_unused]] auto desc = [error localizedDescription];
634 jassertfalse;
635 }
636 }
637 }
638
639private:
640 URL url;
641 bool securityAccessSucceeded = false;
642
643 File getLocalFileAccess (URL& urlToUse)
644 {
645 if (NSData* bookmark = (NSData*) getURLBookmark (urlToUse))
646 {
647 BOOL isBookmarkStale = false;
648 NSError* error = nil;
649
650 auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
651 options: 0
652 relativeToURL: nil
653 bookmarkDataIsStale: &isBookmarkStale
654 error: &error];
655
656 if (error == nil)
657 {
658 securityAccessSucceeded = [nsURL startAccessingSecurityScopedResource];
659
660 if (isBookmarkStale)
661 updateStaleBookmark (nsURL, urlToUse);
662
663 return urlToUse.getLocalFile();
664 }
665
666 [[maybe_unused]] auto desc = [error localizedDescription];
667 jassertfalse;
668 }
669
670 return urlToUse.getLocalFile();
671 }
672
673 void updateStaleBookmark (NSURL* nsURL, URL& juceUrl)
674 {
675 NSError* error = nil;
676
677 NSData* bookmark = [nsURL bookmarkDataWithOptions: NSURLBookmarkCreationSuitableForBookmarkFile
678 includingResourceValuesForKeys: nil
679 relativeToURL: nil
680 error: &error];
681
682 if (error == nil)
683 setURLBookmark (juceUrl, (void*) bookmark);
684 else
685 jassertfalse;
686 }
687};
688#endif
689//==============================================================================
690template <typename Member, typename Item>
691static URL::InputStreamOptions with (URL::InputStreamOptions options, Member&& member, Item&& item)
692{
693 options.*member = std::forward<Item> (item);
694 return options;
695}
696
697URL::InputStreamOptions::InputStreamOptions (ParameterHandling handling) : parameterHandling (handling) {}
698
699 URL::InputStreamOptions URL::InputStreamOptions::withProgressCallback (std::function<bool (int, int)> cb) const
700{
701 return with (*this, &InputStreamOptions::progressCallback, std::move (cb));
702}
703
704 URL::InputStreamOptions URL::InputStreamOptions::withExtraHeaders (const String& headers) const
705{
706 return with (*this, &InputStreamOptions::extraHeaders, headers);
707}
708
709 URL::InputStreamOptions URL::InputStreamOptions::withConnectionTimeoutMs (int timeout) const
710{
711 return with (*this, &InputStreamOptions::connectionTimeOutMs, timeout);
712}
713
714 URL::InputStreamOptions URL::InputStreamOptions::withResponseHeaders (StringPairArray* headers) const
715{
716 return with (*this, &InputStreamOptions::responseHeaders, headers);
717}
718
719 URL::InputStreamOptions URL::InputStreamOptions::withStatusCode (int* status) const
720{
721 return with (*this, &InputStreamOptions::statusCode, status);
722}
723
724 URL::InputStreamOptions URL::InputStreamOptions::withNumRedirectsToFollow (int numRedirects) const
725{
726 return with (*this, &InputStreamOptions::numRedirectsToFollow, numRedirects);
727}
728
729 URL::InputStreamOptions URL::InputStreamOptions::withHttpRequestCmd (const String& cmd) const
730{
731 return with (*this, &InputStreamOptions::httpRequestCmd, cmd);
732}
733
734//==============================================================================
735std::unique_ptr<InputStream> URL::createInputStream (const InputStreamOptions& options) const
736{
737 if (isLocalFile())
738 {
739 #if JUCE_IOS
740 // We may need to refresh the embedded bookmark.
741 return std::make_unique<iOSFileStreamWrapper<FileInputStream>> (const_cast<URL&> (*this));
742 #else
743 return getLocalFile().createInputStream();
744 #endif
745 }
746
747 auto webInputStream = [&]
748 {
749 const auto usePost = options.getParameterHandling() == ParameterHandling::inPostData;
750 auto stream = std::make_unique<WebInputStream> (*this, usePost);
751
752 auto extraHeaders = options.getExtraHeaders();
753
754 if (extraHeaders.isNotEmpty())
755 stream->withExtraHeaders (extraHeaders);
756
757 auto timeout = options.getConnectionTimeoutMs();
758
759 if (timeout != 0)
760 stream->withConnectionTimeout (timeout);
761
762 auto requestCmd = options.getHttpRequestCmd();
763
764 if (requestCmd.isNotEmpty())
765 stream->withCustomRequestCommand (requestCmd);
766
767 stream->withNumRedirectsToFollow (options.getNumRedirectsToFollow());
768
769 return stream;
770 }();
771
772 struct ProgressCallbackCaller final : public WebInputStream::Listener
773 {
774 ProgressCallbackCaller (std::function<bool (int, int)> progressCallbackToUse)
775 : callback (std::move (progressCallbackToUse))
776 {
777 }
778
779 bool postDataSendProgress (WebInputStream&, int bytesSent, int totalBytes) override
780 {
781 return callback (bytesSent, totalBytes);
782 }
783
784 std::function<bool (int, int)> callback;
785 };
786
787 auto callbackCaller = [&options]() -> std::unique_ptr<ProgressCallbackCaller>
788 {
789 if (auto progressCallback = options.getProgressCallback())
790 return std::make_unique<ProgressCallbackCaller> (progressCallback);
791
792 return {};
793 }();
794
795 auto success = webInputStream->connect (callbackCaller.get());
796
797 if (auto* status = options.getStatusCode())
798 *status = webInputStream->getStatusCode();
799
800 if (auto* responseHeaders = options.getResponseHeaders())
801 *responseHeaders = webInputStream->getResponseHeaders();
802
803 if (! success || webInputStream->isError())
804 return nullptr;
805
806 // std::move() needed here for older compilers
807 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
808 return std::move (webInputStream);
809 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
810}
811
812std::unique_ptr<OutputStream> URL::createOutputStream() const
813{
814 #if JUCE_ANDROID
815 if (auto stream = AndroidDocument::fromDocument (*this).createOutputStream())
816 return stream;
817 #endif
818
819 if (isLocalFile())
820 {
821 #if JUCE_IOS
822 // We may need to refresh the embedded bookmark.
823 return std::make_unique<iOSFileStreamWrapper<FileOutputStream>> (const_cast<URL&> (*this));
824 #else
825 return std::make_unique<FileOutputStream> (getLocalFile());
826 #endif
827 }
828
829 return nullptr;
830}
831
832//==============================================================================
833 bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const
834{
835 const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
836 : createInputStream (InputStreamOptions (toHandling (usePostCommand))));
837
838 if (in != nullptr)
839 {
840 in->readIntoMemoryBlock (destData);
841 return true;
842 }
843
844 return false;
845}
846
847 String URL::readEntireTextStream (bool usePostCommand) const
848{
849 const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
850 : createInputStream (InputStreamOptions (toHandling (usePostCommand))));
851
852 if (in != nullptr)
853 return in->readEntireStreamAsString();
854
855 return {};
856}
857
858std::unique_ptr<XmlElement> URL::readEntireXmlStream (bool usePostCommand) const
859{
860 return parseXML (readEntireTextStream (usePostCommand));
861}
862
863//==============================================================================
864 URL URL::withParameter (const String& parameterName,
865 const String& parameterValue) const
866{
867 auto u = *this;
868 u.addParameter (parameterName, parameterValue);
869 return u;
870}
871
872 URL URL::withParameters (const StringPairArray& parametersToAdd) const
873{
874 auto u = *this;
875
876 for (int i = 0; i < parametersToAdd.size(); ++i)
877 u.addParameter (parametersToAdd.getAllKeys()[i],
878 parametersToAdd.getAllValues()[i]);
879
880 return u;
881}
882
883 URL URL::withAnchor (const String& anchorToAdd) const
884{
885 auto u = *this;
886
887 u.anchor = anchorToAdd;
888 return u;
889}
890
891 URL URL::withPOSTData (const String& newPostData) const
892{
893 return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8()));
894}
895
896 URL URL::withPOSTData (const MemoryBlock& newPostData) const
897{
898 auto u = *this;
899 u.postData = newPostData;
900 return u;
901}
902
903URL::Upload::Upload (const String& param, const String& name,
904 const String& mime, const File& f, MemoryBlock* mb)
905 : parameterName (param), filename (name), mimeType (mime), file (f), data (mb)
906{
907 jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
908}
909
910URL URL::withUpload (Upload* const f) const
911{
912 auto u = *this;
913
914 for (int i = u.filesToUpload.size(); --i >= 0;)
915 if (u.filesToUpload.getObjectPointerUnchecked (i)->parameterName == f->parameterName)
916 u.filesToUpload.remove (i);
917
918 u.filesToUpload.add (f);
919 return u;
920}
921
922 URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload,
923 const String& mimeType) const
924{
925 return withUpload (new Upload (parameterName, fileToUpload.getFileName(),
926 mimeType, fileToUpload, nullptr));
927}
928
929 URL URL::withDataToUpload (const String& parameterName, const String& filename,
930 const MemoryBlock& fileContentToUpload, const String& mimeType) const
931{
932 return withUpload (new Upload (parameterName, filename, mimeType, File(),
933 new MemoryBlock (fileContentToUpload)));
934}
935
936//==============================================================================
937 String URL::removeEscapeChars (const String& s)
938{
939 auto result = s.replaceCharacter ('+', ' ');
940
941 if (! result.containsChar ('%'))
942 return result;
943
944 // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
945 // after all the replacements have been made, so that multi-byte chars are handled.
946 Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
947
948 for (int i = 0; i < utf8.size(); ++i)
949 {
950 if (utf8.getUnchecked (i) == '%')
951 {
952 auto hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]);
953 auto hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]);
954
955 if (hexDigit1 >= 0 && hexDigit2 >= 0)
956 {
957 utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
958 utf8.removeRange (i + 1, 2);
959 }
960 }
961 }
962
963 return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
964}
965
966 String URL::addEscapeChars (const String& s, bool isParameter, bool roundBracketsAreLegal)
967{
968 String legalChars (isParameter ? "_-.~"
969 : ",$_-.*!'");
970
971 if (roundBracketsAreLegal)
972 legalChars += "()";
973
974 Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
975
976 for (int i = 0; i < utf8.size(); ++i)
977 {
978 auto c = utf8.getUnchecked (i);
979
980 if (! (CharacterFunctions::isLetterOrDigit (c)
981 || legalChars.containsChar ((juce_wchar) c)))
982 {
983 utf8.set (i, '%');
984 utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]);
985 utf8.insert (++i, "0123456789ABCDEF" [c & 15]);
986 }
987 }
988
989 return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
990}
991
992//==============================================================================
993 bool URL::launchInDefaultBrowser() const
994{
995 auto u = toString (true);
996
997 if (u.containsChar ('@') && ! u.containsChar (':'))
998 u = "mailto:" + u;
999
1000 return Process::openDocument (u, {});
1001}
1002
1003//==============================================================================
1004std::unique_ptr<InputStream> URL::createInputStream (bool usePostCommand,
1005 OpenStreamProgressCallback* cb,
1006 void* context,
1007 String headers,
1008 int timeOutMs,
1009 StringPairArray* responseHeaders,
1010 int* statusCode,
1011 int numRedirectsToFollow,
1012 String httpRequestCmd) const
1013{
1014 std::function<bool (int, int)> callback;
1015
1016 if (cb != nullptr)
1017 callback = [context, cb] (int sent, int total) { return cb (context, sent, total); };
1018
1019 return createInputStream (InputStreamOptions (toHandling (usePostCommand))
1020 .withProgressCallback (std::move (callback))
1021 .withExtraHeaders (headers)
1022 .withConnectionTimeoutMs (timeOutMs)
1023 .withResponseHeaders (responseHeaders)
1024 .withStatusCode (statusCode)
1025 .withNumRedirectsToFollow (numRedirectsToFollow)
1026 .withHttpRequestCmd (httpRequestCmd));
1027}
1028
1029std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation,
1030 String extraHeaders,
1031 DownloadTask::Listener* listener,
1032 bool usePostCommand)
1033{
1034 auto options = DownloadTaskOptions().withExtraHeaders (std::move (extraHeaders))
1035 .withListener (listener)
1036 .withUsePost (usePostCommand);
1037 return downloadToFile (targetLocation, std::move (options));
1038}
1039
1040} // namespace juce
juce::AndroidDocument::fromDocument
static AndroidDocument fromDocument(const URL &documentUrl)
juce::AndroidDocument::createOutputStream
std::unique_ptr< OutputStream > createOutputStream() const
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::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::HeapBlock::get
ElementType * get() const noexcept
Definition juce_HeapBlock.h:185
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::StringArray::add
void add(String stringToAdd)
Definition juce_StringArray.cpp: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::startsWithChar
bool startsWithChar(juce_wchar character) const noexcept
Definition juce_String.cpp:1403
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::String::fromFirstOccurrenceOf
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1565
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::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::WebInputStream
Definition juce_WebInputStream.h:33
juce::URL::DownloadTaskListener
Definition juce_URL.h:441
juce::URL::DownloadTaskListener::finished
virtual void finished(DownloadTask *task, bool success)=0
juce::URL::DownloadTaskListener::progress
virtual void progress(DownloadTask *task, int64 bytesDownloaded, int64 totalLength)
Definition juce_URL.cpp:109