OpenShot Library | OpenShotAudio  0.2.2
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 = (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 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 //==============================================================================
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 
179 void URL::init()
180 {
181  auto i = url.indexOfChar ('?');
182 
183  if (i >= 0)
184  {
185  do
186  {
187  auto nextAmp = url.indexOfChar (i + 1, '&');
188  auto equalsPos = url.indexOfChar (i + 1, '=');
189 
190  if (nextAmp < 0)
191  {
192  addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1) : url.substring (i + 1, equalsPos)),
193  equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1)));
194  }
195  else if (nextAmp > 0 && equalsPos < nextAmp)
196  {
197  addParameter (removeEscapeChars (equalsPos < 0 ? url.substring (i + 1, nextAmp) : url.substring (i + 1, equalsPos)),
198  equalsPos < 0 ? String() : removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
199  }
200 
201  i = nextAmp;
202  }
203  while (i >= 0);
204 
205  url = url.upToFirstOccurrenceOf ("?", false, false);
206  }
207 }
208 
209 URL::URL (const String& u, int) : url (u) {}
210 
212 {
213  return URL (u, 0);
214 }
215 
216 bool URL::operator== (const URL& other) const
217 {
218  return url == other.url
219  && postData == other.postData
220  && parameterNames == other.parameterNames
221  && parameterValues == other.parameterValues
222  && filesToUpload == other.filesToUpload;
223 }
224 
225 bool URL::operator!= (const URL& other) const
226 {
227  return ! operator== (other);
228 }
229 
230 namespace URLHelpers
231 {
232  static String getMangledParameters (const URL& url)
233  {
234  jassert (url.getParameterNames().size() == url.getParameterValues().size());
235  String p;
236 
237  for (int i = 0; i < url.getParameterNames().size(); ++i)
238  {
239  if (i > 0)
240  p << '&';
241 
242  auto val = url.getParameterValues()[i];
243 
244  p << URL::addEscapeChars (url.getParameterNames()[i], true);
245 
246  if (val.isNotEmpty())
247  p << '=' << URL::addEscapeChars (val, true);
248  }
249 
250  return p;
251  }
252 
253  static int findEndOfScheme (const String& url)
254  {
255  int i = 0;
256 
258  || url[i] == '+' || url[i] == '-' || url[i] == '.')
259  ++i;
260 
261  return url.substring (i).startsWith ("://") ? i + 1 : 0;
262  }
263 
264  static int findStartOfNetLocation (const String& url)
265  {
266  int start = findEndOfScheme (url);
267 
268  while (url[start] == '/')
269  ++start;
270 
271  return start;
272  }
273 
274  static int findStartOfPath (const String& url)
275  {
276  return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
277  }
278 
279  static void concatenatePaths (String& path, const String& suffix)
280  {
281  if (! path.endsWithChar ('/'))
282  path << '/';
283 
284  if (suffix.startsWithChar ('/'))
285  path += suffix.substring (1);
286  else
287  path += suffix;
288  }
289 
290  static String removeLastPathSection (const String& url)
291  {
292  auto startOfPath = findStartOfPath (url);
293  auto lastSlash = url.lastIndexOfChar ('/');
294 
295  if (lastSlash > startOfPath && lastSlash == url.length() - 1)
296  return removeLastPathSection (url.dropLastCharacters (1));
297 
298  if (lastSlash < 0)
299  return url;
300 
301  return url.substring (0, std::max (startOfPath, lastSlash));
302  }
303 }
304 
305 void URL::addParameter (const String& name, const String& value)
306 {
307  parameterNames.add (name);
308  parameterValues.add (value);
309 }
310 
311 String URL::toString (bool includeGetParameters) const
312 {
313  if (includeGetParameters)
314  return url + getQueryString();
315 
316  return url;
317 }
318 
319 bool URL::isEmpty() const noexcept
320 {
321  return url.isEmpty();
322 }
323 
324 bool URL::isWellFormed() const
325 {
326  //xxx TODO
327  return url.isNotEmpty();
328 }
329 
331 {
332  return getDomainInternal (false);
333 }
334 
335 String URL::getSubPath (bool includeGetParameters) const
336 {
337  auto startOfPath = URLHelpers::findStartOfPath (url);
338  auto subPath = startOfPath <= 0 ? String()
339  : url.substring (startOfPath);
340 
341  if (includeGetParameters)
342  subPath += getQueryString();
343 
344  return subPath;
345 }
346 
348 {
349  if (parameterNames.size() > 0)
350  return "?" + URLHelpers::getMangledParameters (*this);
351 
352  return {};
353 }
354 
356 {
357  return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
358 }
359 
360 #if ! JUCE_ANDROID
361 bool URL::isLocalFile() const
362 {
363  return getScheme() == "file";
364 }
365 
366 File URL::getLocalFile() const
367 {
368  return fileFromFileSchemeURL (*this);
369 }
370 
371 String URL::getFileName() const
372 {
373  return toString (false).fromLastOccurrenceOf ("/", false, true);
374 }
375 #endif
376 
377 File URL::fileFromFileSchemeURL (const URL& fileURL)
378 {
379  if (! fileURL.isLocalFile())
380  {
381  jassertfalse;
382  return {};
383  }
384 
385  auto path = removeEscapeChars (fileURL.getDomainInternal (true)).replace ("+", "%2B");
386 
387  #if JUCE_WINDOWS
388  bool isUncPath = (! fileURL.url.startsWith ("file:///"));
389  #else
390  path = File::getSeparatorString() + path;
391  #endif
392 
393  auto urlElements = StringArray::fromTokens (fileURL.getSubPath(), "/", "");
394 
395  for (auto urlElement : urlElements)
396  path += File::getSeparatorString() + removeEscapeChars (urlElement.replace ("+", "%2B"));
397 
398  #if JUCE_WINDOWS
399  if (isUncPath)
400  path = "\\\\" + path;
401  #endif
402 
403  return path;
404 }
405 
406 int URL::getPort() const
407 {
408  auto colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
409 
410  return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
411 }
412 
413 URL URL::withNewDomainAndPath (const String& newURL) const
414 {
415  URL u (*this);
416  u.url = newURL;
417  return u;
418 }
419 
420 URL URL::withNewSubPath (const String& newPath) const
421 {
422  URL u (*this);
423 
424  auto startOfPath = URLHelpers::findStartOfPath (url);
425 
426  if (startOfPath > 0)
427  u.url = url.substring (0, startOfPath);
428 
429  URLHelpers::concatenatePaths (u.url, newPath);
430  return u;
431 }
432 
434 {
435  URL u (*this);
436  u.url = URLHelpers::removeLastPathSection (u.url);
437  return u;
438 }
439 
440 URL URL::getChildURL (const String& subPath) const
441 {
442  URL u (*this);
443  URLHelpers::concatenatePaths (u.url, subPath);
444  return u;
445 }
446 
447 void URL::createHeadersAndPostData (String& headers, MemoryBlock& postDataToWrite) const
448 {
449  MemoryOutputStream data (postDataToWrite, false);
450 
451  if (filesToUpload.size() > 0)
452  {
453  // (this doesn't currently support mixing custom post-data with uploads..)
454  jassert (postData.getSize() == 0);
455 
456  auto boundary = String::toHexString (Random::getSystemRandom().nextInt64());
457 
458  headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
459 
460  data << "--" << boundary;
461 
462  for (int i = 0; i < parameterNames.size(); ++i)
463  {
464  data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i]
465  << "\"\r\n\r\n" << parameterValues[i]
466  << "\r\n--" << boundary;
467  }
468 
469  for (auto* f : filesToUpload)
470  {
471  data << "\r\nContent-Disposition: form-data; name=\"" << f->parameterName
472  << "\"; filename=\"" << f->filename << "\"\r\n";
473 
474  if (f->mimeType.isNotEmpty())
475  data << "Content-Type: " << f->mimeType << "\r\n";
476 
477  data << "Content-Transfer-Encoding: binary\r\n\r\n";
478 
479  if (f->data != nullptr)
480  data << *f->data;
481  else
482  data << f->file;
483 
484  data << "\r\n--" << boundary;
485  }
486 
487  data << "--\r\n";
488  }
489  else
490  {
491  data << URLHelpers::getMangledParameters (*this)
492  << postData;
493 
494  // if the user-supplied headers didn't contain a content-type, add one now..
495  if (! headers.containsIgnoreCase ("Content-Type"))
496  headers << "Content-Type: application/x-www-form-urlencoded\r\n";
497 
498  headers << "Content-length: " << (int) data.getDataSize() << "\r\n";
499  }
500 }
501 
502 //==============================================================================
503 bool URL::isProbablyAWebsiteURL (const String& possibleURL)
504 {
505  for (auto* protocol : { "http:", "https:", "ftp:" })
506  if (possibleURL.startsWithIgnoreCase (protocol))
507  return true;
508 
509  if (possibleURL.containsChar ('@') || possibleURL.containsChar (' '))
510  return false;
511 
512  auto topLevelDomain = possibleURL.upToFirstOccurrenceOf ("/", false, false)
513  .fromLastOccurrenceOf (".", false, false);
514 
515  return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
516 }
517 
518 bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
519 {
520  auto atSign = possibleEmailAddress.indexOfChar ('@');
521 
522  return atSign > 0
523  && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
524  && ! possibleEmailAddress.endsWithChar ('.');
525 }
526 
527 String URL::getDomainInternal (bool ignorePort) const
528 {
529  auto start = URLHelpers::findStartOfNetLocation (url);
530  auto end1 = url.indexOfChar (start, '/');
531  auto end2 = ignorePort ? -1 : url.indexOfChar (start, ':');
532 
533  auto end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
534  : ((end1 < 0 || end2 < 0) ? jmax (end1, end2)
535  : jmin (end1, end2));
536  return url.substring (start, end);
537 }
538 
539 #if JUCE_IOS
540 URL::Bookmark::Bookmark (void* bookmarkToUse) : 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 
632  auto desc = [error localizedDescription];
633  ignoreUnused (desc);
634  jassertfalse;
635  }
636 
637  return urlToUse.getLocalFile();
638  }
639 
640  void updateStaleBookmark (NSURL* nsURL, URL& juceUrl)
641  {
642  NSError* error = nil;
643 
644  NSData* bookmark = [nsURL bookmarkDataWithOptions: NSURLBookmarkCreationSuitableForBookmarkFile
645  includingResourceValuesForKeys: nil
646  relativeToURL: nil
647  error: &error];
648 
649  if (error == nil)
650  setURLBookmark (juceUrl, (void*) bookmark);
651  else
652  jassertfalse;
653  }
654 };
655 #endif
656 
657 //==============================================================================
658 InputStream* URL::createInputStream (bool usePostCommand,
659  OpenStreamProgressCallback* progressCallback,
660  void* progressCallbackContext,
661  String headers,
662  int timeOutMs,
663  StringPairArray* responseHeaders,
664  int* statusCode,
665  int numRedirectsToFollow,
666  String httpRequestCmd) const
667 {
668  if (isLocalFile())
669  {
670  #if JUCE_IOS
671  // We may need to refresh the embedded bookmark.
672  return new iOSFileStreamWrapper<FileInputStream> (const_cast<URL&>(*this));
673  #else
674  return getLocalFile().createInputStream();
675  #endif
676  }
677 
678  auto wi = std::make_unique<WebInputStream> (*this, usePostCommand);
679 
680  struct ProgressCallbackCaller : public WebInputStream::Listener
681  {
682  ProgressCallbackCaller (OpenStreamProgressCallback* progressCallbackToUse, void* progressCallbackContextToUse)
683  : callback (progressCallbackToUse), data (progressCallbackContextToUse)
684  {}
685 
686  bool postDataSendProgress (WebInputStream&, int bytesSent, int totalBytes) override
687  {
688  return callback (data, bytesSent, totalBytes);
689  }
690 
691  OpenStreamProgressCallback* callback;
692  void* const data;
693  };
694 
695  std::unique_ptr<ProgressCallbackCaller> callbackCaller
696  (progressCallback != nullptr ? new ProgressCallbackCaller (progressCallback, progressCallbackContext) : nullptr);
697 
698  if (headers.isNotEmpty())
699  wi->withExtraHeaders (headers);
700 
701  if (timeOutMs != 0)
702  wi->withConnectionTimeout (timeOutMs);
703 
704  if (httpRequestCmd.isNotEmpty())
705  wi->withCustomRequestCommand (httpRequestCmd);
706 
707  wi->withNumRedirectsToFollow (numRedirectsToFollow);
708 
709  bool success = wi->connect (callbackCaller.get());
710 
711  if (statusCode != nullptr)
712  *statusCode = wi->getStatusCode();
713 
714  if (responseHeaders != nullptr)
715  *responseHeaders = wi->getResponseHeaders();
716 
717  if (! success || wi->isError())
718  return nullptr;
719 
720  return wi.release();
721 }
722 
723 #if JUCE_ANDROID
724 OutputStream* juce_CreateContentURIOutputStream (const URL&);
725 #endif
726 
728 {
729  if (isLocalFile())
730  {
731  #if JUCE_IOS
732  // We may need to refresh the embedded bookmark.
733  return new iOSFileStreamWrapper<FileOutputStream> (const_cast<URL&> (*this));
734  #else
735  return new FileOutputStream (getLocalFile());
736  #endif
737  }
738 
739  #if JUCE_ANDROID
740  return juce_CreateContentURIOutputStream (*this);
741  #else
742  return nullptr;
743  #endif
744 }
745 
746 //==============================================================================
747 bool URL::readEntireBinaryStream (MemoryBlock& destData, bool usePostCommand) const
748 {
749  const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
750  : createInputStream (usePostCommand));
751 
752  if (in != nullptr)
753  {
754  in->readIntoMemoryBlock (destData);
755  return true;
756  }
757 
758  return false;
759 }
760 
761 String URL::readEntireTextStream (bool usePostCommand) const
762 {
763  const std::unique_ptr<InputStream> in (isLocalFile() ? getLocalFile().createInputStream()
764  : createInputStream (usePostCommand));
765 
766  if (in != nullptr)
767  return in->readEntireStreamAsString();
768 
769  return {};
770 }
771 
772 std::unique_ptr<XmlElement> URL::readEntireXmlStream (bool usePostCommand) const
773 {
774  return parseXML (readEntireTextStream (usePostCommand));
775 }
776 
777 //==============================================================================
778 URL URL::withParameter (const String& parameterName,
779  const String& parameterValue) const
780 {
781  auto u = *this;
782  u.addParameter (parameterName, parameterValue);
783  return u;
784 }
785 
786 URL URL::withParameters (const StringPairArray& parametersToAdd) const
787 {
788  auto u = *this;
789 
790  for (int i = 0; i < parametersToAdd.size(); ++i)
791  u.addParameter (parametersToAdd.getAllKeys()[i],
792  parametersToAdd.getAllValues()[i]);
793 
794  return u;
795 }
796 
797 URL URL::withPOSTData (const String& newPostData) const
798 {
799  return withPOSTData (MemoryBlock (newPostData.toRawUTF8(), newPostData.getNumBytesAsUTF8()));
800 }
801 
802 URL URL::withPOSTData (const MemoryBlock& newPostData) const
803 {
804  auto u = *this;
805  u.postData = newPostData;
806  return u;
807 }
808 
809 URL::Upload::Upload (const String& param, const String& name,
810  const String& mime, const File& f, MemoryBlock* mb)
811  : parameterName (param), filename (name), mimeType (mime), file (f), data (mb)
812 {
813  jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
814 }
815 
816 URL URL::withUpload (Upload* const f) const
817 {
818  auto u = *this;
819 
820  for (int i = u.filesToUpload.size(); --i >= 0;)
821  if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName)
822  u.filesToUpload.remove (i);
823 
824  u.filesToUpload.add (f);
825  return u;
826 }
827 
828 URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload,
829  const String& mimeType) const
830 {
831  return withUpload (new Upload (parameterName, fileToUpload.getFileName(),
832  mimeType, fileToUpload, nullptr));
833 }
834 
835 URL URL::withDataToUpload (const String& parameterName, const String& filename,
836  const MemoryBlock& fileContentToUpload, const String& mimeType) const
837 {
838  return withUpload (new Upload (parameterName, filename, mimeType, File(),
839  new MemoryBlock (fileContentToUpload)));
840 }
841 
842 //==============================================================================
844 {
845  auto result = s.replaceCharacter ('+', ' ');
846 
847  if (! result.containsChar ('%'))
848  return result;
849 
850  // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
851  // after all the replacements have been made, so that multi-byte chars are handled.
852  Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
853 
854  for (int i = 0; i < utf8.size(); ++i)
855  {
856  if (utf8.getUnchecked(i) == '%')
857  {
858  auto hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]);
859  auto hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]);
860 
861  if (hexDigit1 >= 0 && hexDigit2 >= 0)
862  {
863  utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
864  utf8.removeRange (i + 1, 2);
865  }
866  }
867  }
868 
869  return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
870 }
871 
872 String URL::addEscapeChars (const String& s, bool isParameter, bool roundBracketsAreLegal)
873 {
874  String legalChars (isParameter ? "_-.~"
875  : ",$_-.*!'");
876 
877  if (roundBracketsAreLegal)
878  legalChars += "()";
879 
880  Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
881 
882  for (int i = 0; i < utf8.size(); ++i)
883  {
884  auto c = utf8.getUnchecked(i);
885 
887  || legalChars.containsChar ((juce_wchar) c)))
888  {
889  utf8.set (i, '%');
890  utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]);
891  utf8.insert (++i, "0123456789ABCDEF" [c & 15]);
892  }
893  }
894 
895  return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
896 }
897 
898 //==============================================================================
900 {
901  auto u = toString (true);
902 
903  if (u.containsChar ('@') && ! u.containsChar (':'))
904  u = "mailto:" + u;
905 
906  return Process::openDocument (u, {});
907 }
908 
909 } // namespace juce
bool isWellFormed() const
True if it seems to be valid.
Definition: juce_URL.cpp:324
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:366
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:786
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:368
InputStream * createInputStream(bool doPostLikeRequest, OpenStreamProgressCallback *progressCallback=nullptr, void *progressCallbackContext=nullptr, String extraHeaders={}, int connectionTimeOutMs=0, StringPairArray *responseHeaders=nullptr, int *statusCode=nullptr, int numRedirectsToFollow=5, String httpRequestCmd={}) const
Attempts to open a stream that can read from this URL.
Definition: juce_URL.cpp:658
URL getChildURL(const String &subPath) const
Returns a new URL that refers to a sub-path relative to this one.
Definition: juce_URL.cpp:440
const StringArray & getParameterValues() const noexcept
Returns an array of the values of all the URL&#39;s parameters.
Definition: juce_URL.h:244
File getParentDirectory() const
Returns the directory that contains this file or directory.
Definition: juce_File.cpp:360
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.
int getPort() const
Attempts to read a port number from the URL.
Definition: juce_URL.cpp:406
bool operator==(const URL &) const
Compares two URLs.
Definition: juce_URL.cpp:216
bool containsChar(juce_wchar character) const noexcept
Tests whether the string contains a particular character.
URL()
Creates an empty URL.
Definition: juce_URL.cpp:138
String toString(bool includeGetParameters) const
Returns a string version of the URL.
Definition: juce_URL.cpp:311
bool isEmpty() const noexcept
Returns true if the URL is an empty string.
Definition: juce_URL.cpp:319
String dropLastCharacters(int numberToDrop) const
Returns a version of this string with a number of characters removed from the end.
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:127
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:835
std::unique_ptr< XmlElement > readEntireXmlStream(bool usePostCommand=false) const
Tries to download the entire contents of this URL and parse it as XML.
Definition: juce_URL.cpp:772
The JUCE String class!
Definition: juce_String.h:42
FileInputStream * createInputStream() const
Creates a stream to read from this file.
Definition: juce_File.cpp:729
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:355
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:778
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 getQueryString() const
If any parameters are set, returns these URL encoded, including the "?" prefix.
Definition: juce_URL.cpp:347
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:413
URL withNewSubPath(const String &newPath) const
Returns a new version of this URL with a different sub-path.
Definition: juce_URL.cpp:420
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:843
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:211
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:797
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:518
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:1057
const StringArray & getParameterNames() const noexcept
Returns an array of the names of all the URL&#39;s parameters.
Definition: juce_URL.h:230
bool launchInDefaultBrowser() const
Tries to launch the system&#39;s default browser to open the URL.
Definition: juce_URL.cpp:899
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:747
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:330
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:872
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:828
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:739
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.
URL getParentURL() const
Attempts to return a URL which is the parent folder containing this URL.
Definition: juce_URL.cpp:433
bool waitForThreadToExit(int timeOutMilliseconds) const
Waits for the thread to stop.
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:761
String getSubPath(bool includeGetParameters=false) const
Returns the path part of the URL.
Definition: juce_URL.cpp:335
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:372
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.
int length() const noexcept
Returns the number of characters in the 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:503
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:727
bool(void *context, int bytesSent, int totalBytes) OpenStreamProgressCallback
This callback function can be used by the createInputStream() method.
Definition: juce_URL.h:303
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.