OpenShot Library | libopenshot-audio  0.1.9
juce_URL.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 
23 namespace juce
24 {
25 
27  public Thread
28 {
29  FallbackDownloadTask (FileOutputStream* outputStreamToUse,
30  size_t bufferSizeToUse,
31  WebInputStream* streamToUse,
32  URL::DownloadTask::Listener* listenerToUse)
33  : Thread ("DownloadTask thread"),
34  fileStream (outputStreamToUse),
35  stream (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  {
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 = jmin ((int) bufferSize, contentLength < 0 ? std::numeric_limits<int>::max()
66  : static_cast<int> (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->flush();
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  const 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 URL::DownloadTask::Listener::~Listener() {}
111 
112 //==============================================================================
113 URL::DownloadTask* URL::DownloadTask::createFallbackDownloader (const URL& urlToUse,
114  const File& targetFileToUse,
115  const String& extraHeadersToUse,
116  Listener* listenerToUse,
117  bool usePostRequest)
118 {
119  const size_t bufferSize = 0x8000;
120  targetFileToUse.deleteFile();
121 
122  if (auto outputStream = std::unique_ptr<FileOutputStream> (targetFileToUse.createOutputStream (bufferSize)))
123  {
124  std::unique_ptr<WebInputStream> stream (new WebInputStream (urlToUse, usePostRequest));
125  stream->withExtraHeaders (extraHeadersToUse);
126 
127  if (stream->connect (nullptr))
128  return new FallbackDownloadTask (outputStream.release(), bufferSize, stream.release(), listenerToUse);
129  }
130 
131  return nullptr;
132 }
133 
134 URL::DownloadTask::DownloadTask() {}
136 
137 //==============================================================================
138 URL::URL() noexcept {}
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 
175  url = "file://" + url;
176 
177  jassert (isWellFormed());
178 }
179 
180 void URL::init()
181 {
182  auto i = url.indexOfChar ('?');
183 
184  if (i >= 0)
185  {
186  do
187  {
188  auto nextAmp = url.indexOfChar (i + 1, '&');
189  auto equalsPos = url.indexOfChar (i + 1, '=');
190 
191  if (nextAmp < 0)
192  {
193  addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1) : url.substring (i + 1, equalsPos)),
194  equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1)));
195  }
196  else if (nextAmp > 0 && equalsPos < nextAmp)
197  {
198  addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1, nextAmp) : url.substring (i + 1, equalsPos)),
199  equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
200  }
201 
202  i = nextAmp;
203  }
204  while (i >= 0);
205 
206  url = url.upToFirstOccurrenceOf ("?", false, false);
207  }
208 }
209 
210 URL::URL (const String& u, int) : url (u) {}
211 
212 URL::URL (URL&& other)
213  : url (std::move (other.url)),
214  postData (std::move (other.postData)),
215  parameterNames (std::move (other.parameterNames)),
216  parameterValues (std::move (other.parameterValues)),
217  filesToUpload (std::move (other.filesToUpload))
218  #if JUCE_IOS
219  , bookmark (std::move (other.bookmark))
220  #endif
221 {
222 }
223 
224 URL& URL::operator= (URL&& other)
225 {
226  url = std::move (other.url);
227  postData = std::move (other.postData);
228  parameterNames = std::move (other.parameterNames);
229  parameterValues = std::move (other.parameterValues);
230  filesToUpload = std::move (other.filesToUpload);
231  #if JUCE_IOS
232  bookmark = std::move (other.bookmark);
233  #endif
234 
235  return *this;
236 }
237 
239 
241 {
242  return URL (u, 0);
243 }
244 
245 bool URL::operator== (const URL& other) const
246 {
247  return url == other.url
248  && postData == other.postData
249  && parameterNames == other.parameterNames
250  && parameterValues == other.parameterValues
251  && filesToUpload == other.filesToUpload;
252 }
253 
254 bool URL::operator!= (const URL& other) const
255 {
256  return ! operator== (other);
257 }
258 
259 namespace URLHelpers
260 {
261  static String getMangledParameters (const URL& url)
262  {
263  jassert (url.getParameterNames().size() == url.getParameterValues().size());
264  String p;
265 
266  for (int i = 0; i < url.getParameterNames().size(); ++i)
267  {
268  if (i > 0)
269  p << '&';
270 
271  auto val = url.getParameterValues()[i];
272 
273  p << URL::addEscapeChars (url.getParameterNames()[i], true);
274 
275  if (val.isNotEmpty())
276  p << '=' << URL::addEscapeChars (val, true);
277  }
278 
279  return p;
280  }
281 
282  static int findEndOfScheme (const String& url)
283  {
284  int i = 0;
285 
287  || url[i] == '+' || url[i] == '-' || url[i] == '.')
288  ++i;
289 
290  return url.substring (i).startsWith ("://") ? i + 1 : 0;
291  }
292 
293  static int findStartOfNetLocation (const String& url)
294  {
295  int start = findEndOfScheme (url);
296 
297  while (url[start] == '/')
298  ++start;
299 
300  return start;
301  }
302 
303  static int findStartOfPath (const String& url)
304  {
305  return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
306  }
307 
308  static void concatenatePaths (String& path, const String& suffix)
309  {
310  if (! path.endsWithChar ('/'))
311  path << '/';
312 
313  if (suffix.startsWithChar ('/'))
314  path += suffix.substring (1);
315  else
316  path += suffix;
317  }
318 }
319 
320 void URL::addParameter (const String& name, const String& value)
321 {
322  parameterNames.add (name);
323  parameterValues.add (value);
324 }
325 
326 String URL::toString (bool includeGetParameters) const
327 {
328  if (includeGetParameters && parameterNames.size() > 0)
329  return url + "?" + URLHelpers::getMangledParameters (*this);
330 
331  return url;
332 }
333 
334 bool URL::isEmpty() const noexcept
335 {
336  return url.isEmpty();
337 }
338 
339 bool URL::isWellFormed() const
340 {
341  //xxx TODO
342  return url.isNotEmpty();
343 }
344 
346 {
347  return getDomainInternal (false);
348 }
349 
351 {
352  auto startOfPath = URLHelpers::findStartOfPath (url);
353 
354  return startOfPath <= 0 ? String()
355  : url.substring (startOfPath);
356 }
357 
359 {
360  return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
361 }
362 
363 #ifndef JUCE_ANDROID
364 bool URL::isLocalFile() const
365 {
366  return (getScheme() == "file");
367 }
368 
369 File URL::getLocalFile() const
370 {
371  return fileFromFileSchemeURL (*this);
372 }
373 
374 String URL::getFileName() const
375 {
376  return toString (false).fromLastOccurrenceOf ("/", false, true);
377 }
378 #endif
379 
380 File URL::fileFromFileSchemeURL (const URL& fileURL)
381 {
382  if (! fileURL.isLocalFile())
383  {
384  jassertfalse;
385  return {};
386  }
387 
388  auto path = removeEscapeChars (fileURL.getDomainInternal (true)).replace ("+", "%2B");
389 
390  #ifdef JUCE_WINDOWS
391  bool isUncPath = (! fileURL.url.startsWith ("file:///"));
392  #else
393  path = File::getSeparatorString() + path;
394  #endif
395 
396  auto urlElements = StringArray::fromTokens (fileURL.getSubPath(), "/", "");
397 
398  for (auto urlElement : urlElements)
399  path += File::getSeparatorString() + removeEscapeChars (urlElement.replace ("+", "%2B"));
400 
401  #ifdef JUCE_WINDOWS
402  if (isUncPath)
403  path = "\\\\" + path;
404  #endif
405 
406  return path;
407 }
408 
409 int URL::getPort() const
410 {
411  auto colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
412 
413  return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
414 }
415 
416 URL URL::withNewDomainAndPath (const String& newURL) const
417 {
418  URL u (*this);
419  u.url = newURL;
420  return u;
421 }
422 
423 URL URL::withNewSubPath (const String& newPath) const
424 {
425  const int startOfPath = URLHelpers::findStartOfPath (url);
426 
427  URL u (*this);
428 
429  if (startOfPath > 0)
430  u.url = url.substring (0, startOfPath);
431 
432  URLHelpers::concatenatePaths (u.url, newPath);
433  return u;
434 }
435 
436 URL URL::getChildURL (const String& subPath) const
437 {
438  URL u (*this);
439  URLHelpers::concatenatePaths (u.url, subPath);
440  return u;
441 }
442 
443 void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrite) const
444 {
445  MemoryOutputStream data (postDataToWrite, false);
446 
447  if (filesToUpload.size() > 0)
448  {
449  // (this doesn't currently support mixing custom post-data with uploads..)
450  jassert (postData.getSize() == 0);
451 
452  auto boundary = String::toHexString (Random::getSystemRandom().nextInt64());
453 
454  headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
455 
456  data << "--" << boundary;
457 
458  for (int i = 0; i < parameterNames.size(); ++i)
459  {
460  data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i]
461  << "\"\r\n\r\n" << parameterValues[i]
462  << "\r\n--" << boundary;
463  }
464 
465  for (auto* f : filesToUpload)
466  {
467  data << "\r\nContent-Disposition: form-data; name=\"" << f->parameterName
468  << "\"; filename=\"" << f->filename << "\"\r\n";
469 
470  if (f->mimeType.isNotEmpty())
471  data << "Content-Type: " << f->mimeType << "\r\n";
472 
473  data << "Content-Transfer-Encoding: binary\r\n\r\n";
474 
475  if (f->data != nullptr)
476  data << *f->data;
477  else
478  data << f->file;
479 
480  data << "\r\n--" << boundary;
481  }
482 
483  data << "--\r\n";
484  }
485  else
486  {
487  data << URLHelpers::getMangledParameters (*this)
488  << postData;
489 
490  // if the user-supplied headers didn't contain a content-type, add one now..
491  if (! headers.containsIgnoreCase ("Content-Type"))
492  headers << "Content-Type: application/x-www-form-urlencoded\r\n";
493 
494  headers << "Content-length: " << (int) data.getDataSize() << "\r\n";
495  }
496 }
497 
498 //==============================================================================
499 bool URL::isProbablyAWebsiteURL (const String& possibleURL)
500 {
501  static const char* validProtocols[] = { "http:", "ftp:", "https:" };
502 
503  for (auto* protocol : validProtocols)
504  if (possibleURL.startsWithIgnoreCase (protocol))
505  return true;
506 
507  if (possibleURL.containsChar ('@')
508  || possibleURL.containsChar (' '))
509  return false;
510 
511  const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false)
512  .fromLastOccurrenceOf (".", false, false));
513 
514  return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
515 }
516 
517 bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
518 {
519  auto atSign = possibleEmailAddress.indexOfChar ('@');
520 
521  return atSign > 0
522  && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
523  && ! possibleEmailAddress.endsWithChar ('.');
524 }
525 
526 String URL::getDomainInternal (bool ignorePort) const
527 {
528  auto start = URLHelpers::findStartOfNetLocation (url);
529  auto end1 = url.indexOfChar (start, '/');
530  auto end2 = ignorePort ? -1 : url.indexOfChar (start, ':');
531 
532  auto end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
533  : ((end1 < 0 || end2 < 0) ? jmax (end1, end2)
534  : jmin (end1, end2));
535  return url.substring (start, end);
536 }
537 
538 #if JUCE_IOS
539 URL::Bookmark::Bookmark (void* bookmarkToUse)
540  : data (bookmarkToUse)
541 {
542 }
543 
544 URL::Bookmark::~Bookmark()
545 {
546  [(NSData*) data release];
547 }
548 
549 void setURLBookmark (URL& u, void* bookmark)
550 {
551  u.bookmark = new URL::Bookmark (bookmark);
552 }
553 
554 void* getURLBookmark (URL& u)
555 {
556  if (u.bookmark.get() == nullptr)
557  return nullptr;
558 
559  return u.bookmark.get()->data;
560 }
561 
562 template <typename Stream> struct iOSFileStreamWrapperFlush { static void flush (Stream*) {} };
563 template <> struct iOSFileStreamWrapperFlush<FileOutputStream> { static void flush (OutputStream* o) { o->flush(); } };
564 
565 template <typename Stream>
566 class iOSFileStreamWrapper : public Stream
567 {
568 public:
569  iOSFileStreamWrapper (URL& urlToUse)
570  : Stream (getLocalFileAccess (urlToUse)),
571  url (urlToUse)
572  {}
573 
575  {
577 
578  if (NSData* bookmark = (NSData*) getURLBookmark (url))
579  {
580  BOOL isBookmarkStale = false;
581  NSError* error = nil;
582 
583  auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
584  options: 0
585  relativeToURL: nil
586  bookmarkDataIsStale: &isBookmarkStale
587  error: &error];
588 
589  if (error == nil)
590  {
591  if (isBookmarkStale)
592  updateStaleBookmark (nsURL, url);
593 
594  [nsURL stopAccessingSecurityScopedResource];
595  }
596  else
597  {
598  auto desc = [error localizedDescription];
599  ignoreUnused (desc);
600  jassertfalse;
601  }
602  }
603  }
604 
605 private:
606  URL url;
607  bool securityAccessSucceeded = false;
608 
609  File getLocalFileAccess (URL& urlToUse)
610  {
611  if (NSData* bookmark = (NSData*) getURLBookmark (urlToUse))
612  {
613  BOOL isBookmarkStale = false;
614  NSError* error = nil;
615 
616  auto nsURL = [NSURL URLByResolvingBookmarkData: bookmark
617  options: 0
618  relativeToURL: nil
619  bookmarkDataIsStale: &isBookmarkStale
620  error: &error];
621 
622  if (error == nil)
623  {
624  securityAccessSucceeded = [nsURL startAccessingSecurityScopedResource];
625 
626  if (isBookmarkStale)
627  updateStaleBookmark (nsURL, urlToUse);
628 
629  return urlToUse.getLocalFile();
630  }
631  else
632  {
633  auto desc = [error localizedDescription];
634  ignoreUnused (desc);
635  jassertfalse;
636  }
637  }
638 
639  return urlToUse.getLocalFile();
640  }
641 
642  void updateStaleBookmark (NSURL* nsURL, URL& juceUrl)
643  {
644  NSError* error = nil;
645 
646  NSData* bookmark = [nsURL bookmarkDataWithOptions: NSURLBookmarkCreationSuitableForBookmarkFile
647  includingResourceValuesForKeys: nil
648  relativeToURL: nil
649  error: &error];
650 
651  if (error == nil)
652  setURLBookmark (juceUrl, (void*) bookmark);
653  else
654  jassertfalse;
655  }
656 };
657 #endif
658 
659 //==============================================================================
660 InputStream* URL::createInputStream (bool usePostCommand,
661  OpenStreamProgressCallback* progressCallback,
662  void* progressCallbackContext,
663  String headers,
664  int timeOutMs,
665  StringPairArray* responseHeaders,
666  int* statusCode,
667  int numRedirectsToFollow,
668  String httpRequestCmd) const
669 {
670  if (isLocalFile())
671  {
672  #if JUCE_IOS
673  // We may need to refresh the embedded bookmark.
674  return new iOSFileStreamWrapper<FileInputStream> (const_cast<URL&>(*this));
675  #else
676  return getLocalFile().createInputStream();
677  #endif
678 
679  }
680 
681  std::unique_ptr<WebInputStream> wi (new WebInputStream (*this, usePostCommand));
682 
683  struct ProgressCallbackCaller : public WebInputStream::Listener
684  {
685  ProgressCallbackCaller (OpenStreamProgressCallback* progressCallbackToUse, void* progressCallbackContextToUse)
686  : callback (progressCallbackToUse), data (progressCallbackContextToUse)
687  {}
688 
689  bool postDataSendProgress (WebInputStream&, int bytesSent, int totalBytes) override
690  {
691  return callback (data, bytesSent, totalBytes);
692  }
693 
694  OpenStreamProgressCallback* callback;
695  void* const data;
696 
697  // workaround a MSVC 2013 compiler warning
698  ProgressCallbackCaller (const ProgressCallbackCaller& o) : callback (o.callback), data (o.data) { jassertfalse; }
699  ProgressCallbackCaller& operator= (const ProgressCallbackCaller&) { jassertfalse; return *this; }
700  };
701 
702  std::unique_ptr<ProgressCallbackCaller> callbackCaller
703  (progressCallback != nullptr ? new ProgressCallbackCaller (progressCallback, progressCallbackContext) : nullptr);
704 
705  if (headers.isNotEmpty())
706  wi->withExtraHeaders (headers);
707 
708  if (timeOutMs != 0)
709  wi->withConnectionTimeout (timeOutMs);
710 
711  if (httpRequestCmd.isNotEmpty())
712  wi->withCustomRequestCommand (httpRequestCmd);
713 
714  wi->withNumRedirectsToFollow (numRedirectsToFollow);
715 
716  bool success = wi->connect (callbackCaller.get());
717 
718  if (statusCode != nullptr)
719  *statusCode = wi->getStatusCode();
720 
721  if (responseHeaders != nullptr)
722  *responseHeaders = wi->getResponseHeaders();
723 
724  if (! success || wi->isError())
725  return nullptr;
726 
727  return wi.release();
728 }
729 
730 #if JUCE_ANDROID
731 OutputStream* juce_CreateContentURIOutputStream (const URL&);
732 #endif
733 
735 {
736  if (isLocalFile())
737  {
738  #if JUCE_IOS
739  // We may need to refresh the embedded bookmark.
740  return new iOSFileStreamWrapper<FileOutputStream> (const_cast<URL&> (*this));
741  #else
742  return new FileOutputStream (getLocalFile());
743  #endif
744  }
745 
746  #if JUCE_ANDROID
747  return juce_CreateContentURIOutputStream (*this);
748  #else
749  return nullptr;
750  #endif
751 }
752 
753 //==============================================================================
754 bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const
755 {
756  const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
757  : static_cast<InputStream*> (createInputStream (usePostCommand)));
758 
759  if (in != nullptr)
760  {
761  in->readIntoMemoryBlock (destData);
762  return true;
763  }
764 
765  return false;
766 }
767 
768 String URL::readEntireTextStream (bool usePostCommand) const
769 {
770  const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
771  : static_cast<InputStream*> (createInputStream (usePostCommand)));
772 
773  if (in != nullptr)
774  return in->readEntireStreamAsString();
775 
776  return {};
777 }
778 
779 XmlElement* URL::readEntireXmlStream (bool usePostCommand) const
780 {
781  return XmlDocument::parse (readEntireTextStream (usePostCommand));
782 }
783 
784 //==============================================================================
785 URL URL::withParameter (const String& parameterName,
786  const String& parameterValue) const
787 {
788  URL u (*this);
789  u.addParameter (parameterName, parameterValue);
790  return u;
791 }
792 
793 URL URL::withParameters (const StringPairArray& parametersToAdd) const
794 {
795  URL u (*this);
796 
797  for (int i = 0; i < parametersToAdd.size(); ++i)
798  u.addParameter (parametersToAdd.getAllKeys()[i],
799  parametersToAdd.getAllValues()[i]);
800 
801  return u;
802 }
803 
804 URL URL::withPOSTData (const String& newPostData) const
805 {
806  return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8()));
807 }
808 
809 URL URL::withPOSTData (const MemoryBlock& newPostData) const
810 {
811  URL u (*this);
812  u.postData = newPostData;
813  return u;
814 }
815 
816 URL::Upload::Upload (const String& param, const String& name,
817  const String& mime, const File& f, MemoryBlock* mb)
818  : parameterName (param), filename (name), mimeType (mime), file (f), data (mb)
819 {
820  jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
821 }
822 
823 URL URL::withUpload (Upload* const f) const
824 {
825  URL u (*this);
826 
827  for (int i = u.filesToUpload.size(); --i >= 0;)
828  if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName)
829  u.filesToUpload.remove (i);
830 
831  u.filesToUpload.add (f);
832  return u;
833 }
834 
835 URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload,
836  const String& mimeType) const
837 {
838  return withUpload (new Upload (parameterName, fileToUpload.getFileName(),
839  mimeType, fileToUpload, nullptr));
840 }
841 
842 URL URL::withDataToUpload (const String& parameterName, const String& filename,
843  const MemoryBlock& fileContentToUpload, const String& mimeType) const
844 {
845  return withUpload (new Upload (parameterName, filename, mimeType, File(),
846  new MemoryBlock (fileContentToUpload)));
847 }
848 
849 //==============================================================================
851 {
852  auto result = s.replaceCharacter ('+', ' ');
853 
854  if (! result.containsChar ('%'))
855  return result;
856 
857  // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
858  // after all the replacements have been made, so that multi-byte chars are handled.
859  Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
860 
861  for (int i = 0; i < utf8.size(); ++i)
862  {
863  if (utf8.getUnchecked(i) == '%')
864  {
865  auto hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]);
866  auto hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]);
867 
868  if (hexDigit1 >= 0 && hexDigit2 >= 0)
869  {
870  utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
871  utf8.removeRange (i + 1, 2);
872  }
873  }
874  }
875 
876  return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
877 }
878 
879 String URL::addEscapeChars (const String& s, bool isParameter, bool roundBracketsAreLegal)
880 {
881  String legalChars (isParameter ? "_-.~"
882  : ",$_-.*!'");
883 
884  if (roundBracketsAreLegal)
885  legalChars += "()";
886 
887  Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
888 
889  for (int i = 0; i < utf8.size(); ++i)
890  {
891  auto c = utf8.getUnchecked(i);
892 
894  || legalChars.containsChar ((juce_wchar) c)))
895  {
896  utf8.set (i, '%');
897  utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]);
898  utf8.insert (++i, "0123456789ABCDEF" [c & 15]);
899  }
900  }
901 
902  return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
903 }
904 
905 //==============================================================================
907 {
908  auto u = toString (true);
909 
910  if (u.containsChar ('@') && ! u.containsChar (':'))
911  u = "mailto:" + u;
912 
913  return Process::openDocument (u, {});
914 }
915 
916 } // namespace juce
bool isWellFormed() const
True if it seems to be valid.
Definition: juce_URL.cpp:339
void run() override
Must be implemented to perform the thread&#39;s actual code.
Definition: juce_URL.cpp:58
const StringArray & getAllValues() const noexcept
Returns a list of all values in the array.
String getFileName() const
Returns the last section of the pathname.
Definition: juce_File.cpp:346
static Random & getSystemRandom() noexcept
The overhead of creating a new Random object is fairly small, but if you want to avoid it...
Definition: juce_Random.cpp:71
URL withParameters(const StringPairArray &parametersToAdd) const
Returns a copy of this URL, with a set of GET or POST parameters added.
Definition: juce_URL.cpp:793
ObjectClass * getObjectPointerUnchecked(int index) const noexcept
Returns a raw pointer to the object at this index in the array, without checking whether the index is...
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from a given substring.
const char * toRawUTF8() const
Returns a pointer to a UTF-8 version of this string.
bool endsWithChar(juce_wchar character) const noexcept
Tests whether the string ends with a particular character.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
Represents a download task.
Definition: juce_URL.h:356
URL getChildURL(const String &subPath) const
Returns a new URL that refers to a sub-path relative to this one.
Definition: juce_URL.cpp:436
const StringArray & getParameterValues() const noexcept
Returns an array of the values of all the URL&#39;s parameters.
Definition: juce_URL.h:232
XmlElement * readEntireXmlStream(bool usePostCommand=false) const
Tries to download the entire contents of this URL and parse it as XML.
Definition: juce_URL.cpp:779
URL() noexcept
Creates an empty URL.
Definition: juce_URL.cpp:138
File getParentDirectory() const
Returns the directory that contains this file or directory.
Definition: juce_File.cpp:340
virtual ~DownloadTask()
Releases the resources of the download task, unregisters the listener and cancels the download if nec...
Definition: juce_URL.cpp:135
Thread(const String &threadName, size_t threadStackSize=0)
Creates a thread.
Definition: juce_Thread.cpp:26
The base class for streams that read data.
const StringArray & getAllKeys() const noexcept
Returns a list of all keys in the array.
void signalThreadShouldExit()
Sets a flag to tell the thread it should stop.
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
bool deleteFile() const
Deletes a file.
Used to build a tree of elements representing an XML document.
int getPort() const
Attempts to read a port number from the URL.
Definition: juce_URL.cpp:409
bool operator==(const URL &) const
Compares two URLs.
Definition: juce_URL.cpp:245
bool containsChar(juce_wchar character) const noexcept
Tests whether the string contains a particular character.
String toString(bool includeGetParameters) const
Returns a string version of the URL.
Definition: juce_URL.cpp:326
bool isEmpty() const noexcept
Returns true if the URL is an empty string.
Definition: juce_URL.cpp:334
bool isRoot() const
Checks whether the path of this file represents the root of a file system, irrespective of its existe...
Definition: juce_File.cpp:107
URL withDataToUpload(const String &parameterName, const String &filename, const MemoryBlock &fileContentToUpload, const String &mimeType) const
Returns a copy of this URL, with a file-upload type parameter added to it.
Definition: juce_URL.cpp:842
InputStream * createInputStream(bool doPostLikeRequest, OpenStreamProgressCallback *progressCallback=nullptr, void *progressCallbackContext=nullptr, String extraHeaders=String(), int connectionTimeOutMs=0, StringPairArray *responseHeaders=nullptr, int *statusCode=nullptr, int numRedirectsToFollow=5, String httpRequestCmd=String()) const
Attempts to open a stream that can read from this URL.
Definition: juce_URL.cpp:660
The JUCE String class!
Definition: juce_String.h:42
FileInputStream * createInputStream() const
Creates a stream to read from this file.
Definition: juce_File.cpp:709
An InputStream which can be used to read from a given url.
String getScheme() const
Returns the scheme of the URL.
Definition: juce_URL.cpp:358
File getLocalFile() const
Returns the file path of the local file to which this URL refers to.
static bool JUCE_CALLTYPE openDocument(const String &documentURL, const String &parameters)
Tries to launch the OS&#39;s default reader application for a given file or URL.
bool startsWithIgnoreCase(StringRef text) const noexcept
Tests whether the string begins with another string.
int size() const noexcept
Returns the number of strings in the array.
ElementType * get() const noexcept
Returns a raw pointer to the allocated data.
URL withParameter(const String &parameterName, const String &parameterValue) const
Returns a copy of this URL, with a GET or POST parameter added to the end.
Definition: juce_URL.cpp:785
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
Definition: juce_Array.h:256
virtual void flush()=0
If the stream is using a buffer, this will ensure it gets written out to the destination.
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from the last occurrence of a given substring.
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
URL withNewDomainAndPath(const String &newFullPath) const
Returns a new version of this URL with a different domain and path.
Definition: juce_URL.cpp:416
URL withNewSubPath(const String &newPath) const
Returns a new version of this URL with a different sub-path.
Definition: juce_URL.cpp:423
void remove(int indexToRemove)
Removes an object from the array.
static String removeEscapeChars(const String &stringToRemoveEscapeCharsFrom)
Replaces any escape character sequences in a string with their original character codes...
Definition: juce_URL.cpp:850
bool isLocalFile() const
Returns true if this URL refers to a local file.
int size() const noexcept
Returns the current number of objects in the array.
static URL createWithoutParsing(const String &url)
Returns a URL without attempting to remove any embedded parameters from the string.
Definition: juce_URL.cpp:240
URL withPOSTData(const String &postData) const
Returns a copy of this URL, with a block of data to send as the POST data.
Definition: juce_URL.cpp:804
bool containsIgnoreCase(StringRef text) const noexcept
Tests whether the string contains another substring.
static int getHexDigitValue(juce_wchar digit) noexcept
Returns 0 to 16 for &#39;0&#39; to &#39;F", or -1 for characters that aren&#39;t a legal hex digit.
static bool isProbablyAnEmailAddress(const String &possibleEmailAddress)
Takes a guess as to whether a string might be a valid email address.
Definition: juce_URL.cpp:517
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
Encapsulates a thread.
Definition: juce_Thread.h:46
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
Definition: juce_String.h:1055
const StringArray & getParameterNames() const noexcept
Returns an array of the names of all the URL&#39;s parameters.
Definition: juce_URL.h:218
bool launchInDefaultBrowser() const
Tries to launch the system&#39;s default browser to open the URL.
Definition: juce_URL.cpp:906
~URL()
Destructor.
Definition: juce_URL.cpp:238
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Returns an array containing the tokens in a given string.
bool readEntireBinaryStream(MemoryBlock &destData, bool usePostCommand=false) const
Tries to download the entire contents of this URL into a binary data block.
Definition: juce_URL.cpp:754
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
Returns a string with all occurrences of a character replaced with a different one.
Represents a local file or directory.
Definition: juce_File.h:44
The base class for streams that write data to some kind of destination.
Holds a resizable array of primitive or copy-by-value objects.
Definition: juce_Array.h:59
String getDomain() const
Returns just the domain part of the URL.
Definition: juce_URL.cpp:345
static String addEscapeChars(const String &stringToAddEscapeCharsTo, bool isParameter, bool roundBracketsAreLegal=true)
Adds escape sequences to a string to encode any characters that aren&#39;t legal in a URL...
Definition: juce_URL.cpp:879
URL withFileToUpload(const String &parameterName, const File &fileToUpload, const String &mimeType) const
Returns a copy of this URL, with a file-upload type parameter added to it.
Definition: juce_URL.cpp:835
ReferencedType * get() const noexcept
Returns the object that this pointer references.
FileOutputStream * createOutputStream(size_t bufferSize=0x8000) const
Creates a stream to write to this file.
Definition: juce_File.cpp:719
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
virtual void progress(URL::DownloadTask *task, int64 bytesDownloaded, int64 totalLength)
Called periodically by the OS to indicate download progress.
Definition: juce_URL.cpp:109
bool startsWithChar(juce_wchar character) const noexcept
Tests whether the string begins with a particular character.
bool waitForThreadToExit(int timeOutMilliseconds) const
Waits for the thread to stop.
static XmlElement * parse(const File &file)
A handy static method that parses a file.
An output stream that writes into a local file.
static StringRef getSeparatorString()
The system-specific file separator character, as a string.
String readEntireTextStream(bool usePostCommand=false) const
Tries to download the entire contents of this URL as a string.
Definition: juce_URL.cpp:768
int lastIndexOfChar(juce_wchar character) const noexcept
Searches for a character inside this string (working backwards from the end of the string)...
int size() const noexcept
Returns the number of strings in the array.
virtual void finished(URL::DownloadTask *task, bool success)=0
Called when the download has finished.
A container for holding a set of strings which are keyed by another string.
Used to receive callbacks for download progress.
Definition: juce_URL.h:360
Writes data to an internal memory buffer, which grows as required.
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Searches for a character inside this string.
static bool isProbablyAWebsiteURL(const String &possibleURL)
Takes a guess as to whether a string might be a valid website address.
Definition: juce_URL.cpp:499
size_t getNumBytesAsUTF8() const noexcept
Returns the number of bytes required to represent this string as UTF8.
A class to hold a resizable block of raw data.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
static bool isLetterOrDigit(char character) noexcept
Checks whether a character is alphabetic or numeric.
const String & getFullPathName() const noexcept
Returns the complete, absolute path of this file.
Definition: juce_File.h:153
String getFileName() const
Returns the file name.
OutputStream * createOutputStream() const
Attempts to open an output stream to a URL for writing.
Definition: juce_URL.cpp:734
bool(void *context, int bytesSent, int totalBytes) OpenStreamProgressCallback
This callback function can be used by the createInputStream() method.
Definition: juce_URL.h:291
bool startsWith(StringRef text) const noexcept
Tests whether the string begins with another string.
Used to receive callbacks for data send progress.
Represents a URL and has a bunch of useful functions to manipulate it.
Definition: juce_URL.h:41
void startThread()
Starts the thread running.
String getSubPath() const
Returns the path part of the URL.
Definition: juce_URL.cpp:350