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 // (This file gets included by juce_linux_NativeCode.cpp, rather than being
27 // compiled on its own).
28 #if JUCE_INCLUDED_FILE
31 //==============================================================================
32 void MACAddress::findAllAddresses (Array
<MACAddress
>& result
)
34 const int s
= socket (AF_INET
, SOCK_DGRAM
, 0);
39 ifc
.ifc_len
= sizeof (buf
);
41 ioctl (s
, SIOCGIFCONF
, &ifc
);
43 for (unsigned int i
= 0; i
< ifc
.ifc_len
/ sizeof (struct ifreq
); ++i
)
46 strcpy (ifr
.ifr_name
, ifc
.ifc_req
[i
].ifr_name
);
48 if (ioctl (s
, SIOCGIFFLAGS
, &ifr
) == 0
49 && (ifr
.ifr_flags
& IFF_LOOPBACK
) == 0
50 && ioctl (s
, SIOCGIFHWADDR
, &ifr
) == 0)
52 result
.addIfNotAlreadyThere (MACAddress ((const uint8
*) ifr
.ifr_hwaddr
.sa_data
));
61 bool PlatformUtilities::launchEmailWithAttachments (const String
& targetEmailAddress
,
62 const String
& emailSubject
,
63 const String
& bodyText
,
64 const StringArray
& filesToAttach
)
66 jassertfalse
; // xxx todo
72 //==============================================================================
73 class WebInputStream
: public InputStream
76 //==============================================================================
77 WebInputStream (const String
& address_
, bool isPost_
, const MemoryBlock
& postData_
,
78 URL::OpenStreamProgressCallback
* progressCallback
, void* progressCallbackContext
,
79 const String
& headers_
, int timeOutMs_
, StringPairArray
* responseHeaders
)
80 : socketHandle (-1), levelsOfRedirection (0),
81 address (address_
), headers (headers_
), postData (postData_
), position (0),
82 finished (false), isPost (isPost_
), timeOutMs (timeOutMs_
)
84 createConnection (progressCallback
, progressCallbackContext
);
86 if (responseHeaders
!= nullptr && ! isError())
88 for (int i
= 0; i
< headerLines
.size(); ++i
)
90 const String
& headersEntry
= headerLines
[i
];
91 const String
key (headersEntry
.upToFirstOccurrenceOf (": ", false, false));
92 const String
value (headersEntry
.fromFirstOccurrenceOf (": ", false, false));
93 const String
previousValue ((*responseHeaders
) [key
]);
94 responseHeaders
->set (key
, previousValue
.isEmpty() ? value
: (previousValue
+ "," + value
));
104 //==============================================================================
105 bool isError() const { return socketHandle
< 0; }
106 bool isExhausted() { return finished
; }
107 int64
getPosition() { return position
; }
109 int64
getTotalLength()
111 jassertfalse
; //xxx to do
115 int read (void* buffer
, int bytesToRead
)
117 if (finished
|| isError())
122 FD_SET (socketHandle
, &readbits
);
125 tv
.tv_sec
= jmax (1, timeOutMs
/ 1000);
128 if (select (socketHandle
+ 1, &readbits
, 0, 0, &tv
) <= 0)
129 return 0; // (timeout)
131 const int bytesRead
= jmax (0, (int) recv (socketHandle
, buffer
, bytesToRead
, MSG_WAITALL
));
134 position
+= bytesRead
;
138 bool setPosition (int64 wantedPos
)
143 if (wantedPos
!= position
)
147 if (wantedPos
< position
)
151 createConnection (0, 0);
154 skipNextBytes (wantedPos
- position
);
160 //==============================================================================
162 int socketHandle
, levelsOfRedirection
;
163 StringArray headerLines
;
164 String address
, headers
;
165 MemoryBlock postData
;
173 if (socketHandle
>= 0)
174 close (socketHandle
);
177 levelsOfRedirection
= 0;
180 void createConnection (URL::OpenStreamProgressCallback
* progressCallback
, void* progressCallbackContext
)
184 uint32 timeOutTime
= Time::getMillisecondCounter();
187 timeOutTime
+= 60000;
188 else if (timeOutMs
< 0)
189 timeOutTime
= 0xffffffff;
191 timeOutTime
+= timeOutMs
;
193 String hostName
, hostPath
;
195 if (! decomposeURL (address
, hostName
, hostPath
, hostPort
))
198 String serverName
, proxyName
, proxyPath
;
202 const String
proxyURL (getenv ("http_proxy"));
203 if (proxyURL
.startsWithIgnoreCase ("http://"))
205 if (! decomposeURL (proxyURL
, proxyName
, proxyPath
, proxyPort
))
208 serverName
= proxyName
;
213 serverName
= hostName
;
217 struct addrinfo hints
= { 0 };
218 hints
.ai_family
= AF_UNSPEC
;
219 hints
.ai_socktype
= SOCK_STREAM
;
220 hints
.ai_flags
= AI_NUMERICSERV
;
222 struct addrinfo
* result
= nullptr;
223 if (getaddrinfo (serverName
.toUTF8(), String (port
).toUTF8(), &hints
, &result
) != 0 || result
== 0)
226 socketHandle
= socket (result
->ai_family
, result
->ai_socktype
, 0);
228 if (socketHandle
== -1)
230 freeaddrinfo (result
);
234 int receiveBufferSize
= 16384;
235 setsockopt (socketHandle
, SOL_SOCKET
, SO_RCVBUF
, (char*) &receiveBufferSize
, sizeof (receiveBufferSize
));
236 setsockopt (socketHandle
, SOL_SOCKET
, SO_KEEPALIVE
, 0, 0);
239 setsockopt (socketHandle
, SOL_SOCKET
, SO_NOSIGPIPE
, 0, 0);
242 if (connect (socketHandle
, result
->ai_addr
, result
->ai_addrlen
) == -1)
245 freeaddrinfo (result
);
249 freeaddrinfo (result
);
252 const MemoryBlock
requestHeader (createRequestHeader (hostName
, hostPort
, proxyName
, proxyPort
,
253 hostPath
, address
, headers
, postData
, isPost
));
255 if (! sendHeader (socketHandle
, requestHeader
, timeOutTime
, progressCallback
, progressCallbackContext
))
262 String
responseHeader (readResponse (socketHandle
, timeOutTime
));
264 if (responseHeader
.isNotEmpty())
267 headerLines
.addLines (responseHeader
);
269 const int statusCode
= responseHeader
.fromFirstOccurrenceOf (" ", false, false)
270 .substring (0, 3).getIntValue();
272 //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue();
273 //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked");
275 String
location (findHeaderItem (headerLines
, "Location:"));
277 if (statusCode
>= 300 && statusCode
< 400 && location
.isNotEmpty())
279 if (! location
.startsWithIgnoreCase ("http://"))
280 location
= "http://" + location
;
282 if (++levelsOfRedirection
<= 3)
285 createConnection (progressCallback
, progressCallbackContext
);
291 levelsOfRedirection
= 0;
299 //==============================================================================
300 static String
readResponse (const int socketHandle
, const uint32 timeOutTime
)
302 int bytesRead
= 0, numConsecutiveLFs
= 0;
303 MemoryBlock
buffer (1024, true);
305 while (numConsecutiveLFs
< 2 && bytesRead
< 32768
306 && Time::getMillisecondCounter() <= timeOutTime
)
310 FD_SET (socketHandle
, &readbits
);
313 tv
.tv_sec
= jmax (1, (int) (timeOutTime
- Time::getMillisecondCounter()) / 1000);
316 if (select (socketHandle
+ 1, &readbits
, 0, 0, &tv
) <= 0)
317 return String::empty
; // (timeout)
319 buffer
.ensureSize (bytesRead
+ 8, true);
320 char* const dest
= (char*) buffer
.getData() + bytesRead
;
322 if (recv (socketHandle
, dest
, 1, 0) == -1)
323 return String::empty
;
325 const char lastByte
= *dest
;
328 if (lastByte
== '\n')
330 else if (lastByte
!= '\r')
331 numConsecutiveLFs
= 0;
334 const String
header (CharPointer_UTF8 ((const char*) buffer
.getData()));
336 if (header
.startsWithIgnoreCase ("HTTP/"))
337 return header
.trimEnd();
339 return String::empty
;
342 static MemoryBlock
createRequestHeader (const String
& hostName
, const int hostPort
,
343 const String
& proxyName
, const int proxyPort
,
344 const String
& hostPath
, const String
& originalURL
,
345 const String
& headers
, const MemoryBlock
& postData
,
348 String
header (isPost
? "POST " : "GET ");
350 if (proxyName
.isEmpty())
352 header
<< hostPath
<< " HTTP/1.0\r\nHost: "
353 << hostName
<< ':' << hostPort
;
357 header
<< originalURL
<< " HTTP/1.0\r\nHost: "
358 << proxyName
<< ':' << proxyPort
;
361 header
<< "\r\nUser-Agent: JUCE/" << JUCE_MAJOR_VERSION
<< '.' << JUCE_MINOR_VERSION
362 << "\r\nConnection: Close\r\nContent-Length: "
363 << (int) postData
.getSize() << "\r\n"
364 << headers
<< "\r\n";
367 mb
.append (header
.toUTF8(), (int) strlen (header
.toUTF8()));
368 mb
.append (postData
.getData(), postData
.getSize());
373 static bool sendHeader (int socketHandle
, const MemoryBlock
& requestHeader
, const uint32 timeOutTime
,
374 URL::OpenStreamProgressCallback
* progressCallback
, void* progressCallbackContext
)
376 size_t totalHeaderSent
= 0;
378 while (totalHeaderSent
< requestHeader
.getSize())
380 if (Time::getMillisecondCounter() > timeOutTime
)
383 const int numToSend
= jmin (1024, (int) (requestHeader
.getSize() - totalHeaderSent
));
385 if (send (socketHandle
, static_cast <const char*> (requestHeader
.getData()) + totalHeaderSent
, numToSend
, 0) != numToSend
)
388 totalHeaderSent
+= numToSend
;
390 if (progressCallback
!= nullptr && ! progressCallback (progressCallbackContext
, totalHeaderSent
, requestHeader
.getSize()))
397 static bool decomposeURL (const String
& url
, String
& host
, String
& path
, int& port
)
399 if (! url
.startsWithIgnoreCase ("http://"))
402 const int nextSlash
= url
.indexOfChar (7, '/');
403 int nextColon
= url
.indexOfChar (7, ':');
404 if (nextColon
> nextSlash
&& nextSlash
> 0)
409 host
= url
.substring (7, nextColon
);
412 port
= url
.substring (nextColon
+ 1, nextSlash
).getIntValue();
414 port
= url
.substring (nextColon
+ 1).getIntValue();
421 host
= url
.substring (7, nextSlash
);
423 host
= url
.substring (7);
427 path
= url
.substring (nextSlash
);
434 static String
findHeaderItem (const StringArray
& lines
, const String
& itemName
)
436 for (int i
= 0; i
< lines
.size(); ++i
)
437 if (lines
[i
].startsWithIgnoreCase (itemName
))
438 return lines
[i
].substring (itemName
.length()).trim();
440 return String::empty
;
443 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream
);
446 InputStream
* URL::createNativeStream (const String
& address
, bool isPost
, const MemoryBlock
& postData
,
447 OpenStreamProgressCallback
* progressCallback
, void* progressCallbackContext
,
448 const String
& headers
, const int timeOutMs
, StringPairArray
* responseHeaders
)
450 ScopedPointer
<WebInputStream
> wi (new WebInputStream (address
, isPost
, postData
,
451 progressCallback
, progressCallbackContext
,
452 headers
, timeOutMs
, responseHeaders
));
454 return wi
->isError() ? nullptr : wi
.release();