Add remaining files
[juce-lv2.git] / juce / source / src / io / network / juce_URL.cpp
blob15849a13035217b5be45dfa85c0cf655c39fa3c1
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_URL.h"
31 #include "../streams/juce_InputStream.h"
32 #include "../../maths/juce_Random.h"
33 #include "../../core/juce_PlatformUtilities.h"
34 #include "../../text/juce_XmlDocument.h"
35 #include "../../io/streams/juce_MemoryOutputStream.h"
38 //==============================================================================
39 URL::URL()
43 URL::URL (const String& url_)
44 : url (url_)
46 int i = url.indexOfChar ('?');
48 if (i >= 0)
52 const int nextAmp = url.indexOfChar (i + 1, '&');
53 const int equalsPos = url.indexOfChar (i + 1, '=');
55 if (equalsPos > i + 1)
57 if (nextAmp < 0)
59 parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)),
60 removeEscapeChars (url.substring (equalsPos + 1)));
62 else if (nextAmp > 0 && equalsPos < nextAmp)
64 parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)),
65 removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
69 i = nextAmp;
71 while (i >= 0);
73 url = url.upToFirstOccurrenceOf ("?", false, false);
77 URL::URL (const URL& other)
78 : url (other.url),
79 postData (other.postData),
80 parameters (other.parameters),
81 filesToUpload (other.filesToUpload),
82 mimeTypes (other.mimeTypes)
86 URL& URL::operator= (const URL& other)
88 url = other.url;
89 postData = other.postData;
90 parameters = other.parameters;
91 filesToUpload = other.filesToUpload;
92 mimeTypes = other.mimeTypes;
94 return *this;
97 URL::~URL()
101 namespace URLHelpers
103 String getMangledParameters (const StringPairArray& parameters)
105 String p;
107 for (int i = 0; i < parameters.size(); ++i)
109 if (i > 0)
110 p << '&';
112 p << URL::addEscapeChars (parameters.getAllKeys() [i], true)
113 << '='
114 << URL::addEscapeChars (parameters.getAllValues() [i], true);
117 return p;
120 int findStartOfDomain (const String& url)
122 int i = 0;
124 while (CharacterFunctions::isLetterOrDigit (url[i])
125 || url[i] == '+' || url[i] == '-' || url[i] == '.')
126 ++i;
128 return url[i] == ':' ? i + 1 : 0;
131 void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData)
133 MemoryOutputStream data (postData, false);
135 if (url.getFilesToUpload().size() > 0)
137 // need to upload some files, so do it as multi-part...
138 const String boundary (String::toHexString (Random::getSystemRandom().nextInt64()));
140 headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
142 data << "--" << boundary;
144 int i;
145 for (i = 0; i < url.getParameters().size(); ++i)
147 data << "\r\nContent-Disposition: form-data; name=\""
148 << url.getParameters().getAllKeys() [i]
149 << "\"\r\n\r\n"
150 << url.getParameters().getAllValues() [i]
151 << "\r\n--"
152 << boundary;
155 for (i = 0; i < url.getFilesToUpload().size(); ++i)
157 const File file (url.getFilesToUpload().getAllValues() [i]);
158 const String paramName (url.getFilesToUpload().getAllKeys() [i]);
160 data << "\r\nContent-Disposition: form-data; name=\"" << paramName
161 << "\"; filename=\"" << file.getFileName() << "\"\r\n";
163 const String mimeType (url.getMimeTypesOfUploadFiles()
164 .getValue (paramName, String::empty));
166 if (mimeType.isNotEmpty())
167 data << "Content-Type: " << mimeType << "\r\n";
169 data << "Content-Transfer-Encoding: binary\r\n\r\n"
170 << file << "\r\n--" << boundary;
173 data << "--\r\n";
174 data.flush();
176 else
178 data << getMangledParameters (url.getParameters()) << url.getPostData();
179 data.flush();
181 // just a short text attachment, so use simple url encoding..
182 headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: "
183 << (int) postData.getSize() << "\r\n";
188 String URL::toString (const bool includeGetParameters) const
190 if (includeGetParameters && parameters.size() > 0)
191 return url + "?" + URLHelpers::getMangledParameters (parameters);
192 else
193 return url;
196 bool URL::isWellFormed() const
198 //xxx TODO
199 return url.isNotEmpty();
202 String URL::getDomain() const
204 int start = URLHelpers::findStartOfDomain (url);
205 while (url[start] == '/')
206 ++start;
208 const int end1 = url.indexOfChar (start, '/');
209 const int end2 = url.indexOfChar (start, ':');
211 const int end = (end1 < 0 || end2 < 0) ? jmax (end1, end2)
212 : jmin (end1, end2);
214 return url.substring (start, end);
217 String URL::getSubPath() const
219 int start = URLHelpers::findStartOfDomain (url);
220 while (url[start] == '/')
221 ++start;
223 const int startOfPath = url.indexOfChar (start, '/') + 1;
225 return startOfPath <= 0 ? String::empty
226 : url.substring (startOfPath);
229 String URL::getScheme() const
231 return url.substring (0, URLHelpers::findStartOfDomain (url) - 1);
234 const URL URL::withNewSubPath (const String& newPath) const
236 int start = URLHelpers::findStartOfDomain (url);
237 while (url[start] == '/')
238 ++start;
240 const int startOfPath = url.indexOfChar (start, '/') + 1;
242 URL u (*this);
244 if (startOfPath > 0)
245 u.url = url.substring (0, startOfPath);
247 if (! u.url.endsWithChar ('/'))
248 u.url << '/';
250 if (newPath.startsWithChar ('/'))
251 u.url << newPath.substring (1);
252 else
253 u.url << newPath;
255 return u;
258 //==============================================================================
259 bool URL::isProbablyAWebsiteURL (const String& possibleURL)
261 const char* validProtocols[] = { "http:", "ftp:", "https:" };
263 for (int i = 0; i < numElementsInArray (validProtocols); ++i)
264 if (possibleURL.startsWithIgnoreCase (validProtocols[i]))
265 return true;
267 if (possibleURL.containsChar ('@')
268 || possibleURL.containsChar (' '))
269 return false;
271 const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false)
272 .fromLastOccurrenceOf (".", false, false));
274 return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
277 bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
279 const int atSign = possibleEmailAddress.indexOfChar ('@');
281 return atSign > 0
282 && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
283 && (! possibleEmailAddress.endsWithChar ('.'));
286 //==============================================================================
287 InputStream* URL::createInputStream (const bool usePostCommand,
288 OpenStreamProgressCallback* const progressCallback,
289 void* const progressCallbackContext,
290 const String& extraHeaders,
291 const int timeOutMs,
292 StringPairArray* const responseHeaders) const
294 String headers;
295 MemoryBlock headersAndPostData;
297 if (usePostCommand)
298 URLHelpers::createHeadersAndPostData (*this, headers, headersAndPostData);
300 headers += extraHeaders;
302 if (! headers.endsWithChar ('\n'))
303 headers << "\r\n";
305 return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData,
306 progressCallback, progressCallbackContext,
307 headers, timeOutMs, responseHeaders);
310 //==============================================================================
311 bool URL::readEntireBinaryStream (MemoryBlock& destData,
312 const bool usePostCommand) const
314 const ScopedPointer <InputStream> in (createInputStream (usePostCommand));
316 if (in != nullptr)
318 in->readIntoMemoryBlock (destData);
319 return true;
322 return false;
325 String URL::readEntireTextStream (const bool usePostCommand) const
327 const ScopedPointer <InputStream> in (createInputStream (usePostCommand));
329 if (in != nullptr)
330 return in->readEntireStreamAsString();
332 return String::empty;
335 XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const
337 return XmlDocument::parse (readEntireTextStream (usePostCommand));
340 //==============================================================================
341 const URL URL::withParameter (const String& parameterName,
342 const String& parameterValue) const
344 URL u (*this);
345 u.parameters.set (parameterName, parameterValue);
346 return u;
349 const URL URL::withFileToUpload (const String& parameterName,
350 const File& fileToUpload,
351 const String& mimeType) const
353 jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
355 URL u (*this);
356 u.filesToUpload.set (parameterName, fileToUpload.getFullPathName());
357 u.mimeTypes.set (parameterName, mimeType);
358 return u;
361 const URL URL::withPOSTData (const String& postData_) const
363 URL u (*this);
364 u.postData = postData_;
365 return u;
368 const StringPairArray& URL::getParameters() const
370 return parameters;
373 const StringPairArray& URL::getFilesToUpload() const
375 return filesToUpload;
378 const StringPairArray& URL::getMimeTypesOfUploadFiles() const
380 return mimeTypes;
383 //==============================================================================
384 String URL::removeEscapeChars (const String& s)
386 String result (s.replaceCharacter ('+', ' '));
388 if (! result.containsChar ('%'))
389 return result;
391 // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
392 // after all the replacements have been made, so that multi-byte chars are handled.
393 Array<char> utf8 (result.toUTF8().getAddress(), result.getNumBytesAsUTF8());
395 for (int i = 0; i < utf8.size(); ++i)
397 if (utf8.getUnchecked(i) == '%')
399 const int hexDigit1 = CharacterFunctions::getHexDigitValue (utf8 [i + 1]);
400 const int hexDigit2 = CharacterFunctions::getHexDigitValue (utf8 [i + 2]);
402 if (hexDigit1 >= 0 && hexDigit2 >= 0)
404 utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
405 utf8.removeRange (i + 1, 2);
410 return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
413 String URL::addEscapeChars (const String& s, const bool isParameter)
415 const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()"
416 : ",$_-.*!'()");
418 Array<char> utf8 (s.toUTF8().getAddress(), s.getNumBytesAsUTF8());
420 for (int i = 0; i < utf8.size(); ++i)
422 const char c = utf8.getUnchecked(i);
424 if (! (CharacterFunctions::isLetterOrDigit (c)
425 || legalChars.indexOf ((juce_wchar) c) >= 0))
427 if (c == ' ')
429 utf8.set (i, '+');
431 else
433 static const char* const hexDigits = "0123456789abcdef";
435 utf8.set (i, '%');
436 utf8.insert (++i, hexDigits [((uint8) c) >> 4]);
437 utf8.insert (++i, hexDigits [c & 15]);
442 return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
445 //==============================================================================
446 bool URL::launchInDefaultBrowser() const
448 String u (toString (true));
450 if (u.containsChar ('@') && ! u.containsChar (':'))
451 u = "mailto:" + u;
453 return PlatformUtilities::openDocument (u, String::empty);
457 END_JUCE_NAMESPACE