OpenShot Audio Library | OpenShotAudio 0.4.0
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
48 }
49
50 ~FallbackDownloadTask() override
51 {
53 stream->cancel();
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
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() {}
136
137//==============================================================================
139
140URL::URL (const String& u) : url (u)
141{
142 init();
143}
144
145URL::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
217URL::URL (const String& u, int) : url (u) {}
218
220{
221 return URL (u, 0);
222}
223
224bool 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
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
319String URL::toString (bool includeGetParameters) const
320{
321 if (includeGetParameters)
322 return url + getQueryString();
323
324 return url;
325}
326
327bool URL::isEmpty() const noexcept
328{
329 return url.isEmpty();
330}
331
333{
334 //xxx TODO
335 return url.isNotEmpty();
336}
337
339{
340 return getDomainInternal (false);
341}
342
343String 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
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
369{
370 if (anchor.isNotEmpty())
371 return "#" + URL::addEscapeChars (anchor, true);
372
373 return {};
374}
375
377{
378 return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
379}
380
381#if ! JUCE_ANDROID
383{
384 return getScheme() == "file";
385}
386
388{
389 return fileFromFileSchemeURL (*this);
390}
391
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
432int 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
440{
441 URL u (*this);
442 u.url = newURL;
443 return u;
444}
445
446URL 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
460{
461 URL u (*this);
462 u.url = URLHelpers::removeLastPathSection (u.url);
463 return u;
464}
465
466URL 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//==============================================================================
538bool 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
553bool 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
562String 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
700{
701 return with (*this, &InputStreamOptions::progressCallback, std::move (cb));
702}
703
705{
706 return with (*this, &InputStreamOptions::extraHeaders, headers);
707}
708
710{
711 return with (*this, &InputStreamOptions::connectionTimeOutMs, timeout);
712}
713
715{
716 return with (*this, &InputStreamOptions::responseHeaders, headers);
717}
718
720{
721 return with (*this, &InputStreamOptions::statusCode, status);
722}
723
725{
726 return with (*this, &InputStreamOptions::numRedirectsToFollow, numRedirects);
727}
728
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
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//==============================================================================
833bool 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
847String 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//==============================================================================
864URL 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
872URL 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
883URL URL::withAnchor (const String& anchorToAdd) const
884{
885 auto u = *this;
886
887 u.anchor = anchorToAdd;
888 return u;
889}
890
891URL URL::withPOSTData (const String& newPostData) const
892{
893 return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8()));
894}
895
896URL 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
922URL 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
929URL 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//==============================================================================
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
966String 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
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//==============================================================================
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
static AndroidDocument fromDocument(const URL &documentUrl)
std::unique_ptr< OutputStream > createOutputStream() const
ElementType getUnchecked(int index) const
Definition: juce_Array.h:252
int size() const noexcept
Definition: juce_Array.h:215
void removeRange(int startIndex, int numberToRemove)
Definition: juce_Array.h:894
void insert(int indexToInsertAt, ParameterType newElement)
Definition: juce_Array.h:462
ElementType * getRawDataPointer() noexcept
Definition: juce_Array.h:310
void set(int indexToChange, ParameterType newValue)
Definition: juce_Array.h:542
static int getHexDigitValue(juce_wchar digit) noexcept
static bool isLetterOrDigit(char character) noexcept
std::unique_ptr< FileOutputStream > createOutputStream(size_t bufferSize=0x8000) const
Definition: juce_File.cpp:742
const String & getFullPathName() const noexcept
Definition: juce_File.h:153
String getFileName() const
Definition: juce_File.cpp:372
bool isRoot() const
Definition: juce_File.cpp:125
File getParentDirectory() const
Definition: juce_File.cpp:358
std::unique_ptr< FileInputStream > createInputStream() const
Definition: juce_File.cpp:732
bool deleteFile() const
static StringRef getSeparatorString()
bool isEmpty() const noexcept
static bool JUCE_CALLTYPE openDocument(const String &documentURL, const String &parameters)
static Random & getSystemRandom() noexcept
Definition: juce_Random.cpp:67
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
int size() const noexcept
void add(String stringToAdd)
const StringArray & getAllValues() const noexcept
int size() const noexcept
const StringArray & getAllKeys() const noexcept
int indexOfChar(juce_wchar characterToLookFor) const noexcept
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
bool endsWithChar(juce_wchar character) const noexcept
const char * toRawUTF8() const
bool startsWithChar(juce_wchar character) const noexcept
bool startsWith(StringRef text) const noexcept
bool containsChar(juce_wchar character) const noexcept
bool startsWithIgnoreCase(StringRef text) const noexcept
size_t getNumBytesAsUTF8() const noexcept
static String toHexString(IntegerType number)
Definition: juce_String.h:1097
int lastIndexOfChar(juce_wchar character) const noexcept
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
String substring(int startIndex, int endIndex) const
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
bool isNotEmpty() const noexcept
Definition: juce_String.h:316
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
bool waitForThreadToExit(int timeOutMilliseconds) const
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
Definition: juce_Thread.cpp:26
bool startThread()
bool threadShouldExit() const
void signalThreadShouldExit()
auto withExtraHeaders(String value) const
Definition: juce_URL.h:468
InputStreamOptions withExtraHeaders(const String &extraHeaders) const
Definition: juce_URL.cpp:704
InputStreamOptions withResponseHeaders(StringPairArray *responseHeaders) const
Definition: juce_URL.cpp:714
InputStreamOptions withStatusCode(int *statusCode) const
Definition: juce_URL.cpp:719
InputStreamOptions withNumRedirectsToFollow(int numRedirectsToFollow) const
Definition: juce_URL.cpp:724
InputStreamOptions withConnectionTimeoutMs(int connectionTimeoutMs) const
Definition: juce_URL.cpp:709
InputStreamOptions(ParameterHandling parameterHandling)
Definition: juce_URL.cpp:697
InputStreamOptions withProgressCallback(std::function< bool(int bytesSent, int totalBytes)> progressCallback) const
Definition: juce_URL.cpp:699
InputStreamOptions withHttpRequestCmd(const String &httpRequestCmd) const
Definition: juce_URL.cpp:729
URL withParameter(const String &parameterName, const String &parameterValue) const
Definition: juce_URL.cpp:864
static URL createWithoutParsing(const String &url)
Definition: juce_URL.cpp:219
File getLocalFile() const
Definition: juce_URL.cpp:387
bool isWellFormed() const
Definition: juce_URL.cpp:332
bool readEntireBinaryStream(MemoryBlock &destData, bool usePostCommand=false) const
Definition: juce_URL.cpp:833
int getPort() const
Definition: juce_URL.cpp:432
URL withDataToUpload(const String &parameterName, const String &filename, const MemoryBlock &fileContentToUpload, const String &mimeType) const
Definition: juce_URL.cpp:929
String getFileName() const
Definition: juce_URL.cpp:392
URL getChildURL(const String &subPath) const
Definition: juce_URL.cpp:466
static String removeEscapeChars(const String &stringToRemoveEscapeCharsFrom)
Definition: juce_URL.cpp:937
String getAnchorString() const
Definition: juce_URL.cpp:368
static String addEscapeChars(const String &stringToAddEscapeCharsTo, bool isParameter, bool roundBracketsAreLegal=true)
Definition: juce_URL.cpp:966
URL withAnchor(const String &anchor) const
Definition: juce_URL.cpp:883
String toString(bool includeGetParameters) const
Definition: juce_URL.cpp:319
std::unique_ptr< OutputStream > createOutputStream() const
Definition: juce_URL.cpp:812
String getSubPath(bool includeGetParameters=false) const
Definition: juce_URL.cpp:343
URL getParentURL() const
Definition: juce_URL.cpp:459
String getQueryString() const
Definition: juce_URL.cpp:355
URL withNewSubPath(const String &newPath) const
Definition: juce_URL.cpp:446
static bool isProbablyAnEmailAddress(const String &possibleEmailAddress)
Definition: juce_URL.cpp:553
URL withNewDomainAndPath(const String &newFullPath) const
Definition: juce_URL.cpp:439
String readEntireTextStream(bool usePostCommand=false) const
Definition: juce_URL.cpp:847
bool isEmpty() const noexcept
Definition: juce_URL.cpp:327
static bool isProbablyAWebsiteURL(const String &possibleURL)
Definition: juce_URL.cpp:538
std::unique_ptr< InputStream > createInputStream(const InputStreamOptions &options) const
Definition: juce_URL.cpp:735
std::unique_ptr< DownloadTask > downloadToFile(const File &targetLocation, String extraHeaders=String(), DownloadTaskListener *listener=nullptr, bool usePostCommand=false)
Definition: juce_URL.cpp:1029
String getDomain() const
Definition: juce_URL.cpp:338
bool isLocalFile() const
Definition: juce_URL.cpp:382
URL withFileToUpload(const String &parameterName, const File &fileToUpload, const String &mimeType) const
Definition: juce_URL.cpp:922
URL withPOSTData(const String &postData) const
Definition: juce_URL.cpp:891
URL withParameters(const StringPairArray &parametersToAdd) const
Definition: juce_URL.cpp:872
std::unique_ptr< XmlElement > readEntireXmlStream(bool usePostCommand=false) const
Definition: juce_URL.cpp:858
String getScheme() const
Definition: juce_URL.cpp:376
bool operator==(const URL &) const
Definition: juce_URL.cpp:224
bool launchInDefaultBrowser() const
Definition: juce_URL.cpp:993
virtual void progress(DownloadTask *task, int64 bytesDownloaded, int64 totalLength)
Definition: juce_URL.cpp:109