JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_Network_linux.cpp
Go to the documentation of this file.
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
27{
28 #if JUCE_BSD
29 struct ifaddrs* addrs = nullptr;
30
31 if (getifaddrs (&addrs) != -1)
32 {
33 for (auto* i = addrs; i != nullptr; i = i->ifa_next)
34 {
35 if (i->ifa_addr->sa_family == AF_LINK)
36 {
38 MACAddress ma ((const uint8*) (sdl->sdl_data + sdl->sdl_nlen));
39
40 if (! ma.isNull())
41 result.addIfNotAlreadyThere (ma);
42 }
43 }
44
46 }
47 #else
48 auto s = socket (AF_INET, SOCK_DGRAM, 0);
49
50 if (s != -1)
51 {
52 struct ifaddrs* addrs = nullptr;
53
54 if (getifaddrs (&addrs) != -1)
55 {
56 for (auto* i = addrs; i != nullptr; i = i->ifa_next)
57 {
58 struct ifreq ifr;
59 strcpy (ifr.ifr_name, i->ifa_name);
60 ifr.ifr_addr.sa_family = AF_INET;
61
62 if (ioctl (s, SIOCGIFHWADDR, &ifr) == 0)
63 {
64 MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data);
65
66 if (! ma.isNull())
67 result.addIfNotAlreadyThere (ma);
68 }
69 }
70
72 }
73
74 ::close (s);
75 }
76 #endif
77}
78
79
80bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEmailAddress */,
81 const String& /* emailSubject */,
82 const String& /* bodyText */,
83 const StringArray& /* filesToAttach */)
84{
85 jassertfalse; // xxx todo
86 return false;
87}
88
89//==============================================================================
90#if ! JUCE_USE_CURL
92{
93public:
95 : owner (pimplOwner),
96 url (urlToCopy),
97 addParametersToRequestBody (addParametersToBody),
98 hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()),
99 httpRequestCmd (hasBodyDataToSend ? "POST" : "GET")
100 {
101 }
102
103 ~Pimpl()
104 {
105 closeSocket();
106 }
107
108 //==============================================================================
109 // WebInputStream methods
110 void withExtraHeaders (const String& extraHeaders)
111 {
112 if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
113 headers << "\r\n";
114
115 headers << extraHeaders;
116
117 if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
118 headers << "\r\n";
119 }
120
121 void withCustomRequestCommand (const String& customRequestCommand) { httpRequestCmd = customRequestCommand; }
122 void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; }
123 void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; }
124 int getStatusCode() const { return statusCode; }
125 StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); }
126
127 StringPairArray getResponseHeaders() const
128 {
129 StringPairArray responseHeaders;
130
131 if (! isError())
132 {
133 for (int i = 0; i < headerLines.size(); ++i)
134 {
135 auto& headersEntry = headerLines[i];
136 auto key = headersEntry.upToFirstOccurrenceOf (": ", false, false);
137 auto value = headersEntry.fromFirstOccurrenceOf (": ", false, false);
138 auto previousValue = responseHeaders[key];
139 responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
140 }
141 }
142
143 return responseHeaders;
144 }
145
146 bool connect (WebInputStream::Listener* listener)
147 {
148 {
149 const ScopedLock lock (createSocketLock);
150
151 if (hasBeenCancelled)
152 return false;
153 }
154
155 address = url.toString (! addParametersToRequestBody);
156 statusCode = createConnection (listener, numRedirectsToFollow);
157
158 return statusCode != 0;
159 }
160
161 void cancel()
162 {
163 const ScopedLock lock (createSocketLock);
164
165 hasBeenCancelled = true;
166 statusCode = -1;
167 finished = true;
168
169 closeSocket();
170 }
171
172 //==============================================================================
173 bool isError() const { return socketHandle < 0; }
174 bool isExhausted() { return finished; }
175 int64 getPosition() { return position; }
176 int64 getTotalLength() { return contentLength; }
177
178 int read (void* buffer, int bytesToRead)
179 {
180 if (finished || isError())
181 return 0;
182
183 if (isChunked && ! readingChunk)
184 {
185 if (position >= chunkEnd)
186 {
187 const ScopedValueSetter<bool> setter (readingChunk, true, false);
189 char c = 0;
190
191 if (chunkEnd > 0)
192 {
193 if (read (&c, 1) != 1 || c != '\r'
194 || read (&c, 1) != 1 || c != '\n')
195 {
196 finished = true;
197 return 0;
198 }
199 }
200
201 while (chunkLengthBuffer.getDataSize() < 512 && ! (finished || isError()))
202 {
203 if (read (&c, 1) != 1)
204 {
205 finished = true;
206 return 0;
207 }
208
209 if (c == '\r')
210 continue;
211
212 if (c == '\n')
213 break;
214
215 chunkLengthBuffer.writeByte (c);
216 }
217
218 auto chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64();
219
220 if (chunkSize == 0)
221 {
222 finished = true;
223 return 0;
224 }
225
226 chunkEnd += chunkSize;
227 }
228
229 if (bytesToRead > chunkEnd - position)
230 bytesToRead = static_cast<int> (chunkEnd - position);
231 }
232
233 pollfd pfd { socketHandle, POLLIN, 0 };
234
235 if (poll (&pfd, 1, timeOutMs) <= 0)
236 return 0; // (timeout)
237
238 auto bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL));
239
240 if (bytesRead == 0)
241 finished = true;
242
243 if (! readingChunk)
244 position += bytesRead;
245
246 return bytesRead;
247 }
248
249 bool setPosition (int64 wantedPos)
250 {
251 if (isError())
252 return false;
253
254 if (wantedPos != position)
255 {
256 finished = false;
257
258 if (wantedPos < position)
259 return false;
260
261 auto numBytesToSkip = wantedPos - position;
262 auto skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
264
265 while (numBytesToSkip > 0 && ! isExhausted())
266 numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize));
267 }
268
269 return true;
270 }
271
272 //==============================================================================
273 int statusCode = 0;
274
275private:
276 WebInputStream& owner;
277 URL url;
278 int socketHandle = -1, levelsOfRedirection = 0;
279 StringArray headerLines;
280 String address, headers;
281 MemoryBlock postData;
282 int64 contentLength = -1, position = 0;
283 bool finished = false;
284 const bool addParametersToRequestBody, hasBodyDataToSend;
285 int timeOutMs = 0;
286 int numRedirectsToFollow = 5;
287 String httpRequestCmd;
288 int64 chunkEnd = 0;
289 bool isChunked = false, readingChunk = false;
290 CriticalSection closeSocketLock, createSocketLock;
291 bool hasBeenCancelled = false;
292
293 void closeSocket (bool resetLevelsOfRedirection = true)
294 {
295 const ScopedLock lock (closeSocketLock);
296
297 if (socketHandle >= 0)
298 {
299 ::shutdown (socketHandle, SHUT_RDWR);
300 ::close (socketHandle);
301 }
302
303 socketHandle = -1;
304
306 levelsOfRedirection = 0;
307 }
308
309 int createConnection (WebInputStream::Listener* listener, int numRedirects)
310 {
311 closeSocket (false);
312
313 if (hasBodyDataToSend)
314 WebInputStream::createHeadersAndPostData (url,
315 headers,
316 postData,
317 addParametersToRequestBody);
318
320
321 if (timeOutMs == 0)
322 timeOutMs = 30000;
323
324 if (timeOutMs < 0)
325 timeOutTime = 0xffffffff;
326 else
327 timeOutTime += (uint32) timeOutMs;
328
329 String hostName, hostPath;
330 int hostPort;
331
332 if (! decomposeURL (address, hostName, hostPath, hostPort))
333 return 0;
334
336 int proxyPort = 0;
337 int port = 0;
338
339 auto proxyURL = String::fromUTF8 (getenv ("http_proxy"));
340
341 if (proxyURL.startsWithIgnoreCase ("http://"))
342 {
343 if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
344 return 0;
345
347 port = proxyPort;
348 }
349 else
350 {
351 serverName = hostName;
352 port = hostPort;
353 }
354
355 struct addrinfo hints;
357
358 hints.ai_family = AF_UNSPEC;
359 hints.ai_socktype = SOCK_STREAM;
360 hints.ai_flags = AI_NUMERICSERV;
361
362 struct addrinfo* result = nullptr;
363
364 if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == nullptr)
365 return 0;
366
367 {
368 const ScopedLock lock (createSocketLock);
369
370 socketHandle = hasBeenCancelled ? -1
371 : socket (result->ai_family, result->ai_socktype, 0);
372 }
373
374 if (socketHandle == -1)
375 {
376 freeaddrinfo (result);
377 return 0;
378 }
379
380 int receiveBufferSize = 16384;
381 setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
382 setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, nullptr, 0);
383
384 #if JUCE_MAC
385 setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
386 #endif
387
388 if (::connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1)
389 {
390 closeSocket();
391 freeaddrinfo (result);
392 return 0;
393 }
394
395 freeaddrinfo (result);
396
397 {
398 const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address,
399 headers, postData, httpRequestCmd));
400
401 if (! sendHeader (socketHandle, requestHeader, timeOutTime, owner, listener))
402 {
403 closeSocket();
404 return 0;
405 }
406 }
407
408 auto responseHeader = readResponse (timeOutTime);
409 position = 0;
410
411 if (responseHeader.isNotEmpty())
412 {
414
415 auto status = responseHeader.fromFirstOccurrenceOf (" ", false, false)
416 .substring (0, 3).getIntValue();
417
418 auto location = findHeaderItem (headerLines, "Location:");
419
420 if (++levelsOfRedirection <= numRedirects
421 && status >= 300 && status < 400
422 && location.isNotEmpty() && location != address)
423 {
424 if (! (location.startsWithIgnoreCase ("http://")
425 || location.startsWithIgnoreCase ("https://")
426 || location.startsWithIgnoreCase ("ftp://")))
427 {
428 // The following is a bit dodgy. Ideally, we should do a proper transform of the relative URI to a target URI
429 if (location.startsWithChar ('/'))
430 location = URL (address).withNewSubPath (location).toString (true);
431 else
432 location = address + "/" + location;
433 }
434
435 address = location;
436 return createConnection (listener, numRedirects);
437 }
438
439 auto contentLengthString = findHeaderItem (headerLines, "Content-Length:");
440
441 if (contentLengthString.isNotEmpty())
442 contentLength = contentLengthString.getLargeIntValue();
443
444 isChunked = (findHeaderItem (headerLines, "Transfer-Encoding:") == "chunked");
445
446 return status;
447 }
448
449 closeSocket();
450 return 0;
451 }
452
453 //==============================================================================
454 String readResponse (uint32 timeOutTime)
455 {
456 int numConsecutiveLFs = 0;
457 MemoryOutputStream buffer;
458
459 while (numConsecutiveLFs < 2
460 && buffer.getDataSize() < 32768
462 && ! (finished || isError()))
463 {
464 char c = 0;
465
466 if (read (&c, 1) != 1)
467 return {};
468
469 buffer.writeByte (c);
470
471 if (c == '\n')
473 else if (c != '\r')
475 }
476
477 auto header = buffer.toString().trimEnd();
478
479 if (header.startsWithIgnoreCase ("HTTP/"))
480 return header;
481
482 return {};
483 }
484
485 static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value)
486 {
487 if (! headers.containsIgnoreCase (key))
488 dest << "\r\n" << key << ' ' << value;
489 }
490
491 static void writeHost (MemoryOutputStream& dest, const String& httpRequestCmd,
492 const String& path, const String& host, int port)
493 {
494 dest << httpRequestCmd << ' ' << path << " HTTP/1.1\r\nHost: " << host;
495
496 /* HTTP spec 14.23 says that the port number must be included in the header if it is not 80 */
497 if (port != 80)
498 dest << ':' << port;
499 }
500
501 static MemoryBlock createRequestHeader (const String& hostName, int hostPort,
502 const String& proxyName, int proxyPort,
503 const String& hostPath, const String& originalURL,
504 const String& userHeaders, const MemoryBlock& postData,
505 const String& httpRequestCmd)
506 {
507 MemoryOutputStream header;
508
509 if (proxyName.isEmpty())
510 writeHost (header, httpRequestCmd, hostPath, hostName, hostPort);
511 else
512 writeHost (header, httpRequestCmd, originalURL, proxyName, proxyPort);
513
514 writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY (JUCE_MAJOR_VERSION)
515 "." JUCE_STRINGIFY (JUCE_MINOR_VERSION)
516 "." JUCE_STRINGIFY (JUCE_BUILDNUMBER));
517 writeValueIfNotPresent (header, userHeaders, "Connection:", "close");
518
519 const auto postDataSize = postData.getSize();
520 const auto hasPostData = postDataSize > 0;
521
522 if (hasPostData)
523 writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postDataSize));
524
525 if (userHeaders.isNotEmpty())
526 header << "\r\n" << userHeaders;
527
528 header << "\r\n\r\n";
529
530 if (hasPostData)
531 header << postData;
532
533 return header.getMemoryBlock();
534 }
535
536 static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, uint32 timeOutTime,
538 {
539 size_t totalHeaderSent = 0;
540
541 while (totalHeaderSent < requestHeader.getSize())
542 {
544 return false;
545
546 auto numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent));
547
548 if (send (socketHandle, static_cast<const char*> (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend)
549 return false;
550
552
553 if (listener != nullptr && ! listener->postDataSendProgress (pimplOwner, (int) totalHeaderSent, (int) requestHeader.getSize()))
554 return false;
555 }
556
557 return true;
558 }
559
560 static bool decomposeURL (const String& url, String& host, String& path, int& port)
561 {
562 if (! url.startsWithIgnoreCase ("http://"))
563 return false;
564
565 auto nextSlash = url.indexOfChar (7, '/');
566 auto nextColon = url.indexOfChar (7, ':');
567
568 if (nextColon > nextSlash && nextSlash > 0)
569 nextColon = -1;
570
571 if (nextColon >= 0)
572 {
573 host = url.substring (7, nextColon);
574
575 if (nextSlash >= 0)
576 port = url.substring (nextColon + 1, nextSlash).getIntValue();
577 else
578 port = url.substring (nextColon + 1).getIntValue();
579 }
580 else
581 {
582 port = 80;
583
584 if (nextSlash >= 0)
585 host = url.substring (7, nextSlash);
586 else
587 host = url.substring (7);
588 }
589
590 if (nextSlash >= 0)
591 path = url.substring (nextSlash);
592 else
593 path = "/";
594
595 return true;
596 }
597
598 static String findHeaderItem (const StringArray& lines, const String& itemName)
599 {
600 for (int i = 0; i < lines.size(); ++i)
601 if (lines[i].startsWithIgnoreCase (itemName))
602 return lines[i].substring (itemName.length()).trim();
603
604 return {};
605 }
606
608};
609
611{
612 return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options);
613}
614#endif
615
616} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
bool addIfNotAlreadyThere(ParameterType newElement)
Appends a new element at the end of the array as long as the array doesn't already contain it.
Definition juce_Array.h:522
Represents a local file or directory.
Definition juce_File.h:45
Automatically locks and unlocks a mutex object.
Very simple container class to hold a pointer to some data on the heap.
Represents a MAC network card adapter address ID.
static void findAllAddresses(Array< MACAddress > &results)
Populates a list of the MAC addresses of all the available network cards.
A class to hold a resizable block of raw data.
size_t getSize() const noexcept
Returns the block's current allocated size, in bytes.
Writes data to an internal memory buffer, which grows as required.
String toString() const
Attempts to detect the encoding of the data and convert it to a string.
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
MemoryBlock getMemoryBlock() const
Returns a copy of the stream's data as a memory block.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
static bool JUCE_CALLTYPE openEmailWithAttachments(const String &targetEmailAddress, const String &emailSubject, const String &bodyText, const StringArray &filesToAttach)
Tries to launch the OS's default email application to let the user create a message.
Helper class providing an RAII-based mechanism for temporarily setting and then re-setting a value.
A special array for holding a list of strings.
static StringArray fromLines(StringRef stringToBreakUp)
Returns an array containing the lines in a given string.
int size() const noexcept
Returns the number of strings in the array.
void trim()
Deletes any whitespace characters from the starts and ends of all the strings.
A container for holding a set of strings which are keyed by another string.
void set(const String &key, const String &value)
Adds or amends a key/value pair.
The JUCE String class!
Definition juce_String.h:53
bool endsWithChar(juce_wchar character) const noexcept
Tests whether the string ends with a particular character.
bool containsIgnoreCase(StringRef text) const noexcept
Tests whether the string contains another substring.
String trimEnd() const
Returns a copy of this string with any whitespace characters removed from the end.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
Holds options that can be specified when starting a new download with downloadToFile().
Definition juce_URL.h:460
Represents a URL and has a bunch of useful functions to manipulate it.
Definition juce_URL.h:38
String toString(bool includeGetParameters) const
Returns a string version of the URL.
Definition juce_URL.cpp:319
URL withNewSubPath(const String &newPath) const
Returns a new version of this URL with a different sub-path.
Definition juce_URL.cpp:446
std::unique_ptr< DownloadTask > downloadToFile(const File &targetLocation, String extraHeaders=String(), DownloadTaskListener *listener=nullptr, bool usePostCommand=false)
This function is replaced by a new overload accepting a DownloadTaskOptions argument.
Used to receive callbacks for POST data send progress.
virtual bool postDataSendProgress(WebInputStream &request, int bytesSent, int totalBytes)
This method will be called periodically with updates on POST data upload progress.
An InputStream which can be used to read from a given URL.
getaddrinfo
getenv
ioctl
#define JUCE_STRINGIFY(item)
A handy C macro for stringifying any symbol, rather than just a macro parameter.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_CALLTYPE
This macro defines the C calling convention used as the standard for JUCE calls.
#define JUCE_MAJOR_VERSION
Current JUCE version number.
typedef int
JUCE Namespace.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
Definition juce_Memory.h:32
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
long long int64
A platform-independent 64-bit integer type.
poll
recv
send
setsockopt
socket
strcpy
typedef size_t