- added JUCE library code
git-svn-id: http://moon:8086/svn/software/trunk/projects/FsTrack@357 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
IPAddress::IPAddress() noexcept
|
||||
{
|
||||
address[0] = 0; address[1] = 0;
|
||||
address[2] = 0; address[3] = 0;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (const uint8 bytes[4]) noexcept
|
||||
{
|
||||
address[0] = bytes[0]; address[1] = bytes[1];
|
||||
address[2] = bytes[2]; address[3] = bytes[3];
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept
|
||||
{
|
||||
address[0] = a0; address[1] = a1;
|
||||
address[2] = a2; address[3] = a3;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (uint32 n) noexcept
|
||||
{
|
||||
address[0] = (n >> 24);
|
||||
address[1] = (n >> 16) & 255;
|
||||
address[2] = (n >> 8) & 255;
|
||||
address[3] = (n & 255);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (const String& adr)
|
||||
{
|
||||
StringArray tokens;
|
||||
tokens.addTokens (adr, ".", String());
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
address[i] = (uint8) tokens[i].getIntValue();
|
||||
}
|
||||
|
||||
String IPAddress::toString() const
|
||||
{
|
||||
String s ((int) address[0]);
|
||||
|
||||
for (int i = 1; i < 4; ++i)
|
||||
s << '.' << (int) address[i];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
IPAddress IPAddress::any() noexcept { return IPAddress(); }
|
||||
IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); }
|
||||
IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); }
|
||||
|
||||
bool IPAddress::operator== (const IPAddress& other) const noexcept
|
||||
{
|
||||
return address[0] == other.address[0]
|
||||
&& address[1] == other.address[1]
|
||||
&& address[2] == other.address[2]
|
||||
&& address[3] == other.address[3];
|
||||
}
|
||||
|
||||
bool IPAddress::operator!= (const IPAddress& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result)
|
||||
{
|
||||
in_addr_t addr = addr_in->sin_addr.s_addr;
|
||||
|
||||
if (addr != INADDR_NONE)
|
||||
result.addIfNotAlreadyThere (IPAddress (ntohl (addr)));
|
||||
}
|
||||
|
||||
static void findIPAddresses (int sock, Array<IPAddress>& result)
|
||||
{
|
||||
ifconf cfg;
|
||||
HeapBlock<char> buffer;
|
||||
int bufferSize = 1024;
|
||||
|
||||
do
|
||||
{
|
||||
bufferSize *= 2;
|
||||
buffer.calloc ((size_t) bufferSize);
|
||||
|
||||
cfg.ifc_len = bufferSize;
|
||||
cfg.ifc_buf = buffer;
|
||||
|
||||
if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
|
||||
return;
|
||||
|
||||
} while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6)));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in)))
|
||||
{
|
||||
if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses
|
||||
addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result);
|
||||
|
||||
cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
}
|
||||
#else
|
||||
for (size_t i = 0; i < cfg.ifc_len / sizeof (struct ifreq); ++i)
|
||||
{
|
||||
const ifreq& item = cfg.ifc_req[i];
|
||||
|
||||
if (item.ifr_addr.sa_family == AF_INET)
|
||||
addAddress ((const sockaddr_in*) &item.ifr_addr, result);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result)
|
||||
{
|
||||
const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
|
||||
|
||||
if (sock >= 0)
|
||||
{
|
||||
findIPAddresses (sock, result);
|
||||
::close (sock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_IPADDRESS_H_INCLUDED
|
||||
#define JUCE_IPADDRESS_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An IPV4 address.
|
||||
*/
|
||||
class JUCE_API IPAddress
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Populates a list of all the IP addresses that this machine is using. */
|
||||
static void findAllAddresses (Array<IPAddress>& results);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a null address (0.0.0.0). */
|
||||
IPAddress() noexcept;
|
||||
|
||||
/** Creates an address from 4 bytes. */
|
||||
explicit IPAddress (const uint8 bytes[4]) noexcept;
|
||||
|
||||
/** Creates an address from 4 bytes. */
|
||||
IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept;
|
||||
|
||||
/** Creates an address from a packed 32-bit integer, where the MSB is
|
||||
the first number in the address, and the LSB is the last.
|
||||
*/
|
||||
explicit IPAddress (uint32 asNativeEndian32Bit) noexcept;
|
||||
|
||||
/** Parses a string IP address of the form "a.b.c.d". */
|
||||
explicit IPAddress (const String& address);
|
||||
|
||||
/** Returns a dot-separated string in the form "1.2.3.4" */
|
||||
String toString() const;
|
||||
|
||||
/** Returns an address meaning "any" (0.0.0.0) */
|
||||
static IPAddress any() noexcept;
|
||||
|
||||
/** Returns an address meaning "broadcast" (255.255.255.255) */
|
||||
static IPAddress broadcast() noexcept;
|
||||
|
||||
/** Returns an address meaning "localhost" (127.0.0.1) */
|
||||
static IPAddress local() noexcept;
|
||||
|
||||
bool operator== (const IPAddress& other) const noexcept;
|
||||
bool operator!= (const IPAddress& other) const noexcept;
|
||||
|
||||
/** The elements of the IP address. */
|
||||
uint8 address[4];
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_IPADDRESS_H_INCLUDED
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
MACAddress::MACAddress()
|
||||
{
|
||||
zeromem (address, sizeof (address));
|
||||
}
|
||||
|
||||
MACAddress::MACAddress (const MACAddress& other)
|
||||
{
|
||||
memcpy (address, other.address, sizeof (address));
|
||||
}
|
||||
|
||||
MACAddress& MACAddress::operator= (const MACAddress& other)
|
||||
{
|
||||
memcpy (address, other.address, sizeof (address));
|
||||
return *this;
|
||||
}
|
||||
|
||||
MACAddress::MACAddress (const uint8 bytes[6])
|
||||
{
|
||||
memcpy (address, bytes, sizeof (address));
|
||||
}
|
||||
|
||||
String MACAddress::toString() const
|
||||
{
|
||||
String s;
|
||||
|
||||
for (size_t i = 0; i < sizeof (address); ++i)
|
||||
{
|
||||
s << String::toHexString ((int) address[i]).paddedLeft ('0', 2);
|
||||
|
||||
if (i < sizeof (address) - 1)
|
||||
s << '-';
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int64 MACAddress::toInt64() const noexcept
|
||||
{
|
||||
int64 n = 0;
|
||||
|
||||
for (int i = (int) sizeof (address); --i >= 0;)
|
||||
n = (n << 8) | address[i];
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
bool MACAddress::isNull() const noexcept { return toInt64() == 0; }
|
||||
|
||||
bool MACAddress::operator== (const MACAddress& other) const noexcept { return memcmp (address, other.address, sizeof (address)) == 0; }
|
||||
bool MACAddress::operator!= (const MACAddress& other) const noexcept { return ! operator== (other); }
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MACADDRESS_H_INCLUDED
|
||||
#define JUCE_MACADDRESS_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a MAC network card adapter address ID.
|
||||
*/
|
||||
class JUCE_API MACAddress
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Populates a list of the MAC addresses of all the available network cards. */
|
||||
static void findAllAddresses (Array<MACAddress>& results);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a null address (00-00-00-00-00-00). */
|
||||
MACAddress();
|
||||
|
||||
/** Creates a copy of another address. */
|
||||
MACAddress (const MACAddress&);
|
||||
|
||||
/** Creates a copy of another address. */
|
||||
MACAddress& operator= (const MACAddress&);
|
||||
|
||||
/** Creates an address from 6 bytes. */
|
||||
explicit MACAddress (const uint8 bytes[6]);
|
||||
|
||||
/** Returns a pointer to the 6 bytes that make up this address. */
|
||||
const uint8* getBytes() const noexcept { return address; }
|
||||
|
||||
/** Returns a dash-separated string in the form "11-22-33-44-55-66" */
|
||||
String toString() const;
|
||||
|
||||
/** Returns the address in the lower 6 bytes of an int64.
|
||||
|
||||
This uses a little-endian arrangement, with the first byte of the address being
|
||||
stored in the least-significant byte of the result value.
|
||||
*/
|
||||
int64 toInt64() const noexcept;
|
||||
|
||||
/** Returns true if this address is null (00-00-00-00-00-00). */
|
||||
bool isNull() const noexcept;
|
||||
|
||||
bool operator== (const MACAddress&) const noexcept;
|
||||
bool operator!= (const MACAddress&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
uint8 address[6];
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MACADDRESS_H_INCLUDED
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
NamedPipe::NamedPipe()
|
||||
{
|
||||
}
|
||||
|
||||
NamedPipe::~NamedPipe()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool NamedPipe::openExisting (const String& pipeName)
|
||||
{
|
||||
close();
|
||||
|
||||
ScopedWriteLock sl (lock);
|
||||
currentPipeName = pipeName;
|
||||
return openInternal (pipeName, false);
|
||||
}
|
||||
|
||||
bool NamedPipe::isOpen() const
|
||||
{
|
||||
return pimpl != nullptr;
|
||||
}
|
||||
|
||||
bool NamedPipe::createNewPipe (const String& pipeName)
|
||||
{
|
||||
close();
|
||||
|
||||
ScopedWriteLock sl (lock);
|
||||
currentPipeName = pipeName;
|
||||
return openInternal (pipeName, true);
|
||||
}
|
||||
|
||||
String NamedPipe::getName() const
|
||||
{
|
||||
return currentPipeName;
|
||||
}
|
||||
|
||||
// other methods for this class are implemented in the platform-specific files
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_NAMEDPIPE_H_INCLUDED
|
||||
#define JUCE_NAMEDPIPE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A cross-process pipe that can have data written to and read from it.
|
||||
|
||||
Two processes can use NamedPipe objects to exchange blocks of data.
|
||||
|
||||
@see InterprocessConnection
|
||||
*/
|
||||
class JUCE_API NamedPipe
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a NamedPipe. */
|
||||
NamedPipe();
|
||||
|
||||
/** Destructor. */
|
||||
~NamedPipe();
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open a pipe that already exists.
|
||||
Returns true if it succeeds.
|
||||
*/
|
||||
bool openExisting (const String& pipeName);
|
||||
|
||||
/** Tries to create a new pipe.
|
||||
Returns true if it succeeds.
|
||||
*/
|
||||
bool createNewPipe (const String& pipeName);
|
||||
|
||||
/** Closes the pipe, if it's open. */
|
||||
void close();
|
||||
|
||||
/** True if the pipe is currently open. */
|
||||
bool isOpen() const;
|
||||
|
||||
/** Returns the last name that was used to try to open this pipe. */
|
||||
String getName() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Reads data from the pipe.
|
||||
|
||||
This will block until another thread has written enough data into the pipe to fill
|
||||
the number of bytes specified, or until another thread calls the cancelPendingReads()
|
||||
method.
|
||||
|
||||
If the operation fails, it returns -1, otherwise, it will return the number of
|
||||
bytes read.
|
||||
|
||||
If timeOutMilliseconds is less than zero, it will wait indefinitely, otherwise
|
||||
this is a maximum timeout for reading from the pipe.
|
||||
*/
|
||||
int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds);
|
||||
|
||||
/** Writes some data to the pipe.
|
||||
@returns the number of bytes written, or -1 on failure.
|
||||
*/
|
||||
int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
String currentPipeName;
|
||||
ReadWriteLock lock;
|
||||
|
||||
bool openInternal (const String& pipeName, const bool createPipe);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NamedPipe)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_NAMEDPIPE_H_INCLUDED
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable : 4127 4389 4018)
|
||||
#endif
|
||||
|
||||
#ifndef AI_NUMERICSERV // (missing in older Mac SDKs)
|
||||
#define AI_NUMERICSERV 0x1000
|
||||
#endif
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
typedef int juce_socklen_t;
|
||||
typedef SOCKET SocketHandle;
|
||||
#else
|
||||
typedef socklen_t juce_socklen_t;
|
||||
typedef int SocketHandle;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace SocketHelpers
|
||||
{
|
||||
static void initSockets()
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
static bool socketsStarted = false;
|
||||
|
||||
if (! socketsStarted)
|
||||
{
|
||||
socketsStarted = true;
|
||||
|
||||
WSADATA wsaData;
|
||||
const WORD wVersionRequested = MAKEWORD (1, 1);
|
||||
WSAStartup (wVersionRequested, &wsaData);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool resetSocketOptions (const SocketHandle handle, const bool isDatagram, const bool allowBroadcast) noexcept
|
||||
{
|
||||
const int sndBufSize = 65536;
|
||||
const int rcvBufSize = 65536;
|
||||
const int one = 1;
|
||||
|
||||
return handle > 0
|
||||
&& setsockopt (handle, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvBufSize, sizeof (rcvBufSize)) == 0
|
||||
&& setsockopt (handle, SOL_SOCKET, SO_SNDBUF, (const char*) &sndBufSize, sizeof (sndBufSize)) == 0
|
||||
&& (isDatagram ? ((! allowBroadcast) || setsockopt (handle, SOL_SOCKET, SO_BROADCAST, (const char*) &one, sizeof (one)) == 0)
|
||||
: (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (one)) == 0));
|
||||
}
|
||||
|
||||
static bool bindSocketToPort (const SocketHandle handle, const int port) noexcept
|
||||
{
|
||||
if (handle <= 0 || port <= 0)
|
||||
return false;
|
||||
|
||||
struct sockaddr_in servTmpAddr;
|
||||
zerostruct (servTmpAddr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
|
||||
servTmpAddr.sin_family = PF_INET;
|
||||
servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
servTmpAddr.sin_port = htons ((uint16) port);
|
||||
|
||||
return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0;
|
||||
}
|
||||
|
||||
static int readSocket (const SocketHandle handle,
|
||||
void* const destBuffer, const int maxBytesToRead,
|
||||
bool volatile& connected,
|
||||
const bool blockUntilSpecifiedAmountHasArrived) noexcept
|
||||
{
|
||||
int bytesRead = 0;
|
||||
|
||||
while (bytesRead < maxBytesToRead)
|
||||
{
|
||||
int bytesThisTime;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
bytesThisTime = recv (handle, static_cast<char*> (destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0);
|
||||
#else
|
||||
while ((bytesThisTime = (int) ::read (handle, addBytesToPointer (destBuffer, bytesRead),
|
||||
(size_t) (maxBytesToRead - bytesRead))) < 0
|
||||
&& errno == EINTR
|
||||
&& connected)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bytesThisTime <= 0 || ! connected)
|
||||
{
|
||||
if (bytesRead == 0)
|
||||
bytesRead = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bytesRead += bytesThisTime;
|
||||
|
||||
if (! blockUntilSpecifiedAmountHasArrived)
|
||||
break;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
static int waitForReadiness (const SocketHandle handle, const bool forReading, const int timeoutMsecs) noexcept
|
||||
{
|
||||
struct timeval timeout;
|
||||
struct timeval* timeoutp;
|
||||
|
||||
if (timeoutMsecs >= 0)
|
||||
{
|
||||
timeout.tv_sec = timeoutMsecs / 1000;
|
||||
timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
|
||||
timeoutp = &timeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeoutp = 0;
|
||||
}
|
||||
|
||||
fd_set rset, wset;
|
||||
FD_ZERO (&rset);
|
||||
FD_SET (handle, &rset);
|
||||
FD_ZERO (&wset);
|
||||
FD_SET (handle, &wset);
|
||||
|
||||
fd_set* const prset = forReading ? &rset : nullptr;
|
||||
fd_set* const pwset = forReading ? nullptr : &wset;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
if (select ((int) handle + 1, prset, pwset, 0, timeoutp) < 0)
|
||||
return -1;
|
||||
#else
|
||||
{
|
||||
int result;
|
||||
while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0
|
||||
&& errno == EINTR)
|
||||
{
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
int opt;
|
||||
juce_socklen_t len = sizeof (opt);
|
||||
|
||||
if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0
|
||||
|| opt != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return FD_ISSET (handle, forReading ? &rset : &wset) ? 1 : 0;
|
||||
}
|
||||
|
||||
static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
u_long nonBlocking = shouldBlock ? 0 : (u_long) 1;
|
||||
return ioctlsocket (handle, FIONBIO, &nonBlocking) == 0;
|
||||
#else
|
||||
int socketFlags = fcntl (handle, F_GETFL, 0);
|
||||
|
||||
if (socketFlags == -1)
|
||||
return false;
|
||||
|
||||
if (shouldBlock)
|
||||
socketFlags &= ~O_NONBLOCK;
|
||||
else
|
||||
socketFlags |= O_NONBLOCK;
|
||||
|
||||
return fcntl (handle, F_SETFL, socketFlags) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool connectSocket (int volatile& handle,
|
||||
const bool isDatagram,
|
||||
struct addrinfo** const serverAddress,
|
||||
const String& hostName,
|
||||
const int portNumber,
|
||||
const int timeOutMillisecs) noexcept
|
||||
{
|
||||
struct addrinfo hints;
|
||||
zerostruct (hints);
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
struct addrinfo* info = nullptr;
|
||||
if (getaddrinfo (hostName.toUTF8(), String (portNumber).toUTF8(), &hints, &info) != 0
|
||||
|| info == nullptr)
|
||||
return false;
|
||||
|
||||
if (handle < 0)
|
||||
handle = (int) socket (info->ai_family, info->ai_socktype, 0);
|
||||
|
||||
if (handle < 0)
|
||||
{
|
||||
freeaddrinfo (info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDatagram)
|
||||
{
|
||||
if (*serverAddress != nullptr)
|
||||
freeaddrinfo (*serverAddress);
|
||||
|
||||
*serverAddress = info;
|
||||
return true;
|
||||
}
|
||||
|
||||
setSocketBlockingState (handle, false);
|
||||
const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen);
|
||||
freeaddrinfo (info);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
#else
|
||||
if (errno == EINPROGRESS)
|
||||
#endif
|
||||
{
|
||||
if (waitForReadiness (handle, false, timeOutMillisecs) != 1)
|
||||
{
|
||||
setSocketBlockingState (handle, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSocketBlockingState (handle, true);
|
||||
resetSocketOptions (handle, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void makeReusable (int handle) noexcept
|
||||
{
|
||||
const int reuse = 1;
|
||||
setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StreamingSocket::StreamingSocket()
|
||||
: portNumber (0),
|
||||
handle (-1),
|
||||
connected (false),
|
||||
isListener (false)
|
||||
{
|
||||
SocketHelpers::initSockets();
|
||||
}
|
||||
|
||||
StreamingSocket::StreamingSocket (const String& host, int portNum, int h)
|
||||
: hostName (host),
|
||||
portNumber (portNum),
|
||||
handle (h),
|
||||
connected (true),
|
||||
isListener (false)
|
||||
{
|
||||
SocketHelpers::initSockets();
|
||||
SocketHelpers::resetSocketOptions (h, false, false);
|
||||
}
|
||||
|
||||
StreamingSocket::~StreamingSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int StreamingSocket::read (void* destBuffer, const int maxBytesToRead,
|
||||
const bool blockUntilSpecifiedAmountHasArrived)
|
||||
{
|
||||
return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
||||
connected, blockUntilSpecifiedAmountHasArrived)
|
||||
: -1;
|
||||
}
|
||||
|
||||
int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite)
|
||||
{
|
||||
if (isListener || ! connected)
|
||||
return -1;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0);
|
||||
#else
|
||||
int result;
|
||||
|
||||
while ((result = (int) ::write (handle, sourceBuffer, (size_t) numBytesToWrite)) < 0
|
||||
&& errno == EINTR)
|
||||
{
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int StreamingSocket::waitUntilReady (const bool readyForReading,
|
||||
const int timeoutMsecs) const
|
||||
{
|
||||
return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs)
|
||||
: -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool StreamingSocket::bindToPort (const int port)
|
||||
{
|
||||
return SocketHelpers::bindSocketToPort (handle, port);
|
||||
}
|
||||
|
||||
bool StreamingSocket::connect (const String& remoteHostName,
|
||||
const int remotePortNumber,
|
||||
const int timeOutMillisecs)
|
||||
{
|
||||
if (isListener)
|
||||
{
|
||||
jassertfalse; // a listener socket can't connect to another one!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connected)
|
||||
close();
|
||||
|
||||
hostName = remoteHostName;
|
||||
portNumber = remotePortNumber;
|
||||
isListener = false;
|
||||
|
||||
connected = SocketHelpers::connectSocket (handle, false, nullptr, remoteHostName,
|
||||
remotePortNumber, timeOutMillisecs);
|
||||
|
||||
if (! (connected && SocketHelpers::resetSocketOptions (handle, false, false)))
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamingSocket::close()
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (handle != SOCKET_ERROR || connected)
|
||||
closesocket (handle);
|
||||
|
||||
connected = false;
|
||||
#else
|
||||
if (connected)
|
||||
{
|
||||
connected = false;
|
||||
|
||||
if (isListener)
|
||||
{
|
||||
// need to do this to interrupt the accept() function..
|
||||
StreamingSocket temp;
|
||||
temp.connect (IPAddress::local().toString(), portNumber, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (handle != -1)
|
||||
{
|
||||
::shutdown (handle, SHUT_RDWR);
|
||||
::close (handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
hostName.clear();
|
||||
portNumber = 0;
|
||||
handle = -1;
|
||||
isListener = false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool StreamingSocket::createListener (const int newPortNumber, const String& localHostName)
|
||||
{
|
||||
if (connected)
|
||||
close();
|
||||
|
||||
hostName = "listener";
|
||||
portNumber = newPortNumber;
|
||||
isListener = true;
|
||||
|
||||
struct sockaddr_in servTmpAddr;
|
||||
zerostruct (servTmpAddr);
|
||||
|
||||
servTmpAddr.sin_family = PF_INET;
|
||||
servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
|
||||
if (localHostName.isNotEmpty())
|
||||
servTmpAddr.sin_addr.s_addr = ::inet_addr (localHostName.toUTF8());
|
||||
|
||||
servTmpAddr.sin_port = htons ((uint16) portNumber);
|
||||
|
||||
handle = (int) socket (AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (handle < 0)
|
||||
return false;
|
||||
|
||||
#if ! JUCE_WINDOWS // on windows, adding this option produces behaviour different to posix
|
||||
SocketHelpers::makeReusable (handle);
|
||||
#endif
|
||||
|
||||
if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0
|
||||
|| listen (handle, SOMAXCONN) < 0)
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
StreamingSocket* StreamingSocket::waitForNextConnection() const
|
||||
{
|
||||
// To call this method, you first have to use createListener() to
|
||||
// prepare this socket as a listener.
|
||||
jassert (isListener || ! connected);
|
||||
|
||||
if (connected && isListener)
|
||||
{
|
||||
struct sockaddr_storage address;
|
||||
juce_socklen_t len = sizeof (address);
|
||||
const int newSocket = (int) accept (handle, (struct sockaddr*) &address, &len);
|
||||
|
||||
if (newSocket >= 0 && connected)
|
||||
return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
|
||||
portNumber, newSocket);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool StreamingSocket::isLocal() const noexcept
|
||||
{
|
||||
return hostName == "127.0.0.1";
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadcast)
|
||||
: portNumber (0),
|
||||
handle (-1),
|
||||
connected (true),
|
||||
allowBroadcast (canBroadcast),
|
||||
serverAddress (nullptr)
|
||||
{
|
||||
SocketHelpers::initSockets();
|
||||
|
||||
handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
|
||||
SocketHelpers::makeReusable (handle);
|
||||
bindToPort (localPortNumber);
|
||||
}
|
||||
|
||||
DatagramSocket::DatagramSocket (const String& host, const int portNum,
|
||||
const int h, const int localPortNumber)
|
||||
: hostName (host),
|
||||
portNumber (portNum),
|
||||
handle (h),
|
||||
connected (true),
|
||||
allowBroadcast (false),
|
||||
serverAddress (nullptr)
|
||||
{
|
||||
SocketHelpers::initSockets();
|
||||
|
||||
SocketHelpers::resetSocketOptions (h, true, allowBroadcast);
|
||||
bindToPort (localPortNumber);
|
||||
}
|
||||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
close();
|
||||
|
||||
if (serverAddress != nullptr)
|
||||
freeaddrinfo (static_cast <struct addrinfo*> (serverAddress));
|
||||
}
|
||||
|
||||
void DatagramSocket::close()
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
closesocket (handle);
|
||||
connected = false;
|
||||
#else
|
||||
connected = false;
|
||||
::close (handle);
|
||||
#endif
|
||||
|
||||
hostName.clear();
|
||||
portNumber = 0;
|
||||
handle = -1;
|
||||
}
|
||||
|
||||
bool DatagramSocket::bindToPort (const int port)
|
||||
{
|
||||
return SocketHelpers::bindSocketToPort (handle, port);
|
||||
}
|
||||
|
||||
bool DatagramSocket::connect (const String& remoteHostName,
|
||||
const int remotePortNumber,
|
||||
const int timeOutMillisecs)
|
||||
{
|
||||
if (connected)
|
||||
close();
|
||||
|
||||
hostName = remoteHostName;
|
||||
portNumber = remotePortNumber;
|
||||
|
||||
connected = SocketHelpers::connectSocket (handle, true, (struct addrinfo**) &serverAddress,
|
||||
remoteHostName, remotePortNumber,
|
||||
timeOutMillisecs);
|
||||
|
||||
if (! (connected && SocketHelpers::resetSocketOptions (handle, true, allowBroadcast)))
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DatagramSocket* DatagramSocket::waitForNextConnection() const
|
||||
{
|
||||
while (waitUntilReady (true, -1) == 1)
|
||||
{
|
||||
struct sockaddr_storage address;
|
||||
juce_socklen_t len = sizeof (address);
|
||||
char buf[1];
|
||||
|
||||
if (recvfrom (handle, buf, 0, 0, (struct sockaddr*) &address, &len) > 0)
|
||||
return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
|
||||
ntohs (((struct sockaddr_in*) &address)->sin_port),
|
||||
-1, -1);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int DatagramSocket::waitUntilReady (const bool readyForReading,
|
||||
const int timeoutMsecs) const
|
||||
{
|
||||
return connected ? SocketHelpers::waitForReadiness (handle, readyForReading, timeoutMsecs)
|
||||
: -1;
|
||||
}
|
||||
|
||||
int DatagramSocket::read (void* destBuffer, const int maxBytesToRead, const bool blockUntilSpecifiedAmountHasArrived)
|
||||
{
|
||||
return connected ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead,
|
||||
connected, blockUntilSpecifiedAmountHasArrived)
|
||||
: -1;
|
||||
}
|
||||
|
||||
int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite)
|
||||
{
|
||||
// You need to call connect() first to set the server address..
|
||||
jassert (serverAddress != nullptr && connected);
|
||||
|
||||
return connected ? (int) sendto (handle, (const char*) sourceBuffer,
|
||||
(size_t) numBytesToWrite, 0,
|
||||
static_cast <const struct addrinfo*> (serverAddress)->ai_addr,
|
||||
(juce_socklen_t) static_cast <const struct addrinfo*> (serverAddress)->ai_addrlen)
|
||||
: -1;
|
||||
}
|
||||
|
||||
bool DatagramSocket::isLocal() const noexcept
|
||||
{
|
||||
return hostName == "127.0.0.1";
|
||||
}
|
||||
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SOCKET_H_INCLUDED
|
||||
#define JUCE_SOCKET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper for a streaming (TCP) socket.
|
||||
|
||||
This allows low-level use of sockets; for an easier-to-use messaging layer on top of
|
||||
sockets, you could also try the InterprocessConnection class.
|
||||
|
||||
@see DatagramSocket, InterprocessConnection, InterprocessConnectionServer
|
||||
*/
|
||||
class JUCE_API StreamingSocket
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an uninitialised socket.
|
||||
|
||||
To connect it, use the connect() method, after which you can read() or write()
|
||||
to it.
|
||||
|
||||
To wait for other sockets to connect to this one, the createListener() method
|
||||
enters "listener" mode, and can be used to spawn new sockets for each connection
|
||||
that comes along.
|
||||
*/
|
||||
StreamingSocket();
|
||||
|
||||
/** Destructor. */
|
||||
~StreamingSocket();
|
||||
|
||||
//==============================================================================
|
||||
/** Binds the socket to the specified local port.
|
||||
|
||||
@returns true on success; false may indicate that another socket is already bound
|
||||
on the same port
|
||||
*/
|
||||
bool bindToPort (int localPortNumber);
|
||||
|
||||
/** Tries to connect the socket to hostname:port.
|
||||
|
||||
If timeOutMillisecs is 0, then this method will block until the operating system
|
||||
rejects the connection (which could take a long time).
|
||||
|
||||
@returns true if it succeeds.
|
||||
@see isConnected
|
||||
*/
|
||||
bool connect (const String& remoteHostname,
|
||||
int remotePortNumber,
|
||||
int timeOutMillisecs = 3000);
|
||||
|
||||
/** True if the socket is currently connected. */
|
||||
bool isConnected() const noexcept { return connected; }
|
||||
|
||||
/** Closes the connection. */
|
||||
void close();
|
||||
|
||||
/** Returns the name of the currently connected host. */
|
||||
const String& getHostName() const noexcept { return hostName; }
|
||||
|
||||
/** Returns the port number that's currently open. */
|
||||
int getPort() const noexcept { return portNumber; }
|
||||
|
||||
/** True if the socket is connected to this machine rather than over the network. */
|
||||
bool isLocal() const noexcept;
|
||||
|
||||
/** Returns the OS's socket handle that's currently open. */
|
||||
int getRawSocketHandle() const noexcept { return handle; }
|
||||
|
||||
//==============================================================================
|
||||
/** Waits until the socket is ready for reading or writing.
|
||||
|
||||
If readyForReading is true, it will wait until the socket is ready for
|
||||
reading; if false, it will wait until it's ready for writing.
|
||||
|
||||
If the timeout is < 0, it will wait forever, or else will give up after
|
||||
the specified time.
|
||||
|
||||
If the socket is ready on return, this returns 1. If it times-out before
|
||||
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
||||
*/
|
||||
int waitUntilReady (bool readyForReading,
|
||||
int timeoutMsecs) const;
|
||||
|
||||
/** Reads bytes from the socket.
|
||||
|
||||
If blockUntilSpecifiedAmountHasArrived is true, the method will block until
|
||||
maxBytesToRead bytes have been read, (or until an error occurs). If this
|
||||
flag is false, the method will return as much data as is currently available
|
||||
without blocking.
|
||||
|
||||
@returns the number of bytes read, or -1 if there was an error.
|
||||
@see waitUntilReady
|
||||
*/
|
||||
int read (void* destBuffer, int maxBytesToRead,
|
||||
bool blockUntilSpecifiedAmountHasArrived);
|
||||
|
||||
/** Writes bytes to the socket from a buffer.
|
||||
|
||||
Note that this method will block unless you have checked the socket is ready
|
||||
for writing before calling it (see the waitUntilReady() method).
|
||||
|
||||
@returns the number of bytes written, or -1 if there was an error.
|
||||
*/
|
||||
int write (const void* sourceBuffer, int numBytesToWrite);
|
||||
|
||||
//==============================================================================
|
||||
/** Puts this socket into "listener" mode.
|
||||
|
||||
When in this mode, your thread can call waitForNextConnection() repeatedly,
|
||||
which will spawn new sockets for each new connection, so that these can
|
||||
be handled in parallel by other threads.
|
||||
|
||||
@param portNumber the port number to listen on
|
||||
@param localHostName the interface address to listen on - pass an empty
|
||||
string to listen on all addresses
|
||||
@returns true if it manages to open the socket successfully.
|
||||
|
||||
@see waitForNextConnection
|
||||
*/
|
||||
bool createListener (int portNumber, const String& localHostName = String());
|
||||
|
||||
/** When in "listener" mode, this waits for a connection and spawns it as a new
|
||||
socket.
|
||||
|
||||
The object that gets returned will be owned by the caller.
|
||||
|
||||
This method can only be called after using createListener().
|
||||
|
||||
@see createListener
|
||||
*/
|
||||
StreamingSocket* waitForNextConnection() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String hostName;
|
||||
int volatile portNumber, handle;
|
||||
bool connected, isListener;
|
||||
|
||||
StreamingSocket (const String& hostname, int portNumber, int handle);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper for a datagram (UDP) socket.
|
||||
|
||||
This allows low-level use of sockets; for an easier-to-use messaging layer on top of
|
||||
sockets, you could also try the InterprocessConnection class.
|
||||
|
||||
@see StreamingSocket, InterprocessConnection, InterprocessConnectionServer
|
||||
*/
|
||||
class JUCE_API DatagramSocket
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates an (uninitialised) datagram socket.
|
||||
|
||||
The localPortNumber is the port on which to bind this socket. If this value is 0,
|
||||
the port number is assigned by the operating system.
|
||||
|
||||
To use the socket for sending, call the connect() method. This will not immediately
|
||||
make a connection, but will save the destination you've provided. After this, you can
|
||||
call read() or write().
|
||||
|
||||
If enableBroadcasting is true, the socket will be allowed to send broadcast messages
|
||||
(may require extra privileges on linux)
|
||||
|
||||
To wait for other sockets to connect to this one, call waitForNextConnection().
|
||||
*/
|
||||
DatagramSocket (int localPortNumber,
|
||||
bool enableBroadcasting = false);
|
||||
|
||||
/** Destructor. */
|
||||
~DatagramSocket();
|
||||
|
||||
//==============================================================================
|
||||
/** Binds the socket to the specified local port.
|
||||
|
||||
@returns true on success; false may indicate that another socket is already bound
|
||||
on the same port
|
||||
*/
|
||||
bool bindToPort (int localPortNumber);
|
||||
|
||||
/** Tries to connect the socket to hostname:port.
|
||||
|
||||
If timeOutMillisecs is 0, then this method will block until the operating system
|
||||
rejects the connection (which could take a long time).
|
||||
|
||||
@returns true if it succeeds.
|
||||
@see isConnected
|
||||
*/
|
||||
bool connect (const String& remoteHostname,
|
||||
int remotePortNumber,
|
||||
int timeOutMillisecs = 3000);
|
||||
|
||||
/** True if the socket is currently connected. */
|
||||
bool isConnected() const noexcept { return connected; }
|
||||
|
||||
/** Closes the connection. */
|
||||
void close();
|
||||
|
||||
/** Returns the name of the currently connected host. */
|
||||
const String& getHostName() const noexcept { return hostName; }
|
||||
|
||||
/** Returns the port number that's currently open. */
|
||||
int getPort() const noexcept { return portNumber; }
|
||||
|
||||
/** True if the socket is connected to this machine rather than over the network. */
|
||||
bool isLocal() const noexcept;
|
||||
|
||||
/** Returns the OS's socket handle that's currently open. */
|
||||
int getRawSocketHandle() const noexcept { return handle; }
|
||||
|
||||
//==============================================================================
|
||||
/** Waits until the socket is ready for reading or writing.
|
||||
|
||||
If readyForReading is true, it will wait until the socket is ready for
|
||||
reading; if false, it will wait until it's ready for writing.
|
||||
|
||||
If the timeout is < 0, it will wait forever, or else will give up after
|
||||
the specified time.
|
||||
|
||||
If the socket is ready on return, this returns 1. If it times-out before
|
||||
the socket becomes ready, it returns 0. If an error occurs, it returns -1.
|
||||
*/
|
||||
int waitUntilReady (bool readyForReading,
|
||||
int timeoutMsecs) const;
|
||||
|
||||
/** Reads bytes from the socket.
|
||||
|
||||
If blockUntilSpecifiedAmountHasArrived is true, the method will block until
|
||||
maxBytesToRead bytes have been read, (or until an error occurs). If this
|
||||
flag is false, the method will return as much data as is currently available
|
||||
without blocking.
|
||||
|
||||
@returns the number of bytes read, or -1 if there was an error.
|
||||
@see waitUntilReady
|
||||
*/
|
||||
int read (void* destBuffer, int maxBytesToRead,
|
||||
bool blockUntilSpecifiedAmountHasArrived);
|
||||
|
||||
/** Writes bytes to the socket from a buffer.
|
||||
|
||||
Note that this method will block unless you have checked the socket is ready
|
||||
for writing before calling it (see the waitUntilReady() method).
|
||||
|
||||
@returns the number of bytes written, or -1 if there was an error.
|
||||
*/
|
||||
int write (const void* sourceBuffer, int numBytesToWrite);
|
||||
|
||||
//==============================================================================
|
||||
/** This waits for incoming data to be sent, and returns a socket that can be used
|
||||
to read it.
|
||||
|
||||
The object that gets returned is owned by the caller, and can't be used for
|
||||
sending, but can be used to read the data.
|
||||
*/
|
||||
DatagramSocket* waitForNextConnection() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String hostName;
|
||||
int volatile portNumber, handle;
|
||||
bool connected, allowBroadcast;
|
||||
void* serverAddress;
|
||||
|
||||
DatagramSocket (const String& hostname, int portNumber, int handle, int localPortNumber);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SOCKET_H_INCLUDED
|
||||
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
URL::URL()
|
||||
{
|
||||
}
|
||||
|
||||
URL::URL (const String& u) : url (u)
|
||||
{
|
||||
int i = url.indexOfChar ('?');
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
const int nextAmp = url.indexOfChar (i + 1, '&');
|
||||
const int equalsPos = url.indexOfChar (i + 1, '=');
|
||||
|
||||
if (equalsPos > i + 1)
|
||||
{
|
||||
if (nextAmp < 0)
|
||||
{
|
||||
addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)),
|
||||
removeEscapeChars (url.substring (equalsPos + 1)));
|
||||
}
|
||||
else if (nextAmp > 0 && equalsPos < nextAmp)
|
||||
{
|
||||
addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)),
|
||||
removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
|
||||
}
|
||||
}
|
||||
|
||||
i = nextAmp;
|
||||
}
|
||||
while (i >= 0);
|
||||
|
||||
url = url.upToFirstOccurrenceOf ("?", false, false);
|
||||
}
|
||||
}
|
||||
|
||||
URL::URL (const String& u, int) : url (u) {}
|
||||
|
||||
URL URL::createWithoutParsing (const String& u)
|
||||
{
|
||||
return URL (u, 0);
|
||||
}
|
||||
|
||||
URL::URL (const URL& other)
|
||||
: url (other.url),
|
||||
postData (other.postData),
|
||||
parameterNames (other.parameterNames),
|
||||
parameterValues (other.parameterValues),
|
||||
filesToUpload (other.filesToUpload)
|
||||
{
|
||||
}
|
||||
|
||||
URL& URL::operator= (const URL& other)
|
||||
{
|
||||
url = other.url;
|
||||
postData = other.postData;
|
||||
parameterNames = other.parameterNames;
|
||||
parameterValues = other.parameterValues;
|
||||
filesToUpload = other.filesToUpload;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool URL::operator== (const URL& other) const
|
||||
{
|
||||
return url == other.url
|
||||
&& postData == other.postData
|
||||
&& parameterNames == other.parameterNames
|
||||
&& parameterValues == other.parameterValues
|
||||
&& filesToUpload == other.filesToUpload;
|
||||
}
|
||||
|
||||
bool URL::operator!= (const URL& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
URL::~URL()
|
||||
{
|
||||
}
|
||||
|
||||
namespace URLHelpers
|
||||
{
|
||||
static String getMangledParameters (const URL& url)
|
||||
{
|
||||
jassert (url.getParameterNames().size() == url.getParameterValues().size());
|
||||
String p;
|
||||
|
||||
for (int i = 0; i < url.getParameterNames().size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
p << '&';
|
||||
|
||||
p << URL::addEscapeChars (url.getParameterNames()[i], true)
|
||||
<< '='
|
||||
<< URL::addEscapeChars (url.getParameterValues()[i], true);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int findEndOfScheme (const String& url)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (CharacterFunctions::isLetterOrDigit (url[i])
|
||||
|| url[i] == '+' || url[i] == '-' || url[i] == '.')
|
||||
++i;
|
||||
|
||||
return url[i] == ':' ? i + 1 : 0;
|
||||
}
|
||||
|
||||
static int findStartOfNetLocation (const String& url)
|
||||
{
|
||||
int start = findEndOfScheme (url);
|
||||
while (url[start] == '/')
|
||||
++start;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static int findStartOfPath (const String& url)
|
||||
{
|
||||
return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
|
||||
}
|
||||
|
||||
static void concatenatePaths (String& path, const String& suffix)
|
||||
{
|
||||
if (! path.endsWithChar ('/'))
|
||||
path << '/';
|
||||
|
||||
if (suffix.startsWithChar ('/'))
|
||||
path += suffix.substring (1);
|
||||
else
|
||||
path += suffix;
|
||||
}
|
||||
}
|
||||
|
||||
void URL::addParameter (const String& name, const String& value)
|
||||
{
|
||||
parameterNames.add (name);
|
||||
parameterValues.add (value);
|
||||
}
|
||||
|
||||
String URL::toString (const bool includeGetParameters) const
|
||||
{
|
||||
if (includeGetParameters && parameterNames.size() > 0)
|
||||
return url + "?" + URLHelpers::getMangledParameters (*this);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
bool URL::isEmpty() const noexcept
|
||||
{
|
||||
return url.isEmpty();
|
||||
}
|
||||
|
||||
bool URL::isWellFormed() const
|
||||
{
|
||||
//xxx TODO
|
||||
return url.isNotEmpty();
|
||||
}
|
||||
|
||||
String URL::getDomain() const
|
||||
{
|
||||
const int start = URLHelpers::findStartOfNetLocation (url);
|
||||
const int end1 = url.indexOfChar (start, '/');
|
||||
const int end2 = url.indexOfChar (start, ':');
|
||||
|
||||
const int end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
|
||||
: ((end1 < 0 || end2 < 0) ? jmax (end1, end2)
|
||||
: jmin (end1, end2));
|
||||
return url.substring (start, end);
|
||||
}
|
||||
|
||||
String URL::getSubPath() const
|
||||
{
|
||||
const int startOfPath = URLHelpers::findStartOfPath (url);
|
||||
|
||||
return startOfPath <= 0 ? String()
|
||||
: url.substring (startOfPath);
|
||||
}
|
||||
|
||||
String URL::getScheme() const
|
||||
{
|
||||
return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
|
||||
}
|
||||
|
||||
int URL::getPort() const
|
||||
{
|
||||
const int colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
|
||||
|
||||
return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
|
||||
}
|
||||
|
||||
URL URL::withNewSubPath (const String& newPath) const
|
||||
{
|
||||
const int startOfPath = URLHelpers::findStartOfPath (url);
|
||||
|
||||
URL u (*this);
|
||||
|
||||
if (startOfPath > 0)
|
||||
u.url = url.substring (0, startOfPath);
|
||||
|
||||
URLHelpers::concatenatePaths (u.url, newPath);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::getChildURL (const String& subPath) const
|
||||
{
|
||||
URL u (*this);
|
||||
URLHelpers::concatenatePaths (u.url, subPath);
|
||||
return u;
|
||||
}
|
||||
|
||||
void URL::createHeadersAndPostData (String& headers, MemoryBlock& headersAndPostData) const
|
||||
{
|
||||
MemoryOutputStream data (headersAndPostData, false);
|
||||
|
||||
if (filesToUpload.size() > 0)
|
||||
{
|
||||
// (this doesn't currently support mixing custom post-data with uploads..)
|
||||
jassert (postData.isEmpty());
|
||||
|
||||
const String boundary (String::toHexString (Random::getSystemRandom().nextInt64()));
|
||||
|
||||
headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
|
||||
|
||||
data << "--" << boundary;
|
||||
|
||||
for (int i = 0; i < parameterNames.size(); ++i)
|
||||
{
|
||||
data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i]
|
||||
<< "\"\r\n\r\n" << parameterValues[i]
|
||||
<< "\r\n--" << boundary;
|
||||
}
|
||||
|
||||
for (int i = 0; i < filesToUpload.size(); ++i)
|
||||
{
|
||||
const Upload& f = *filesToUpload.getObjectPointerUnchecked(i);
|
||||
|
||||
data << "\r\nContent-Disposition: form-data; name=\"" << f.parameterName
|
||||
<< "\"; filename=\"" << f.filename << "\"\r\n";
|
||||
|
||||
if (f.mimeType.isNotEmpty())
|
||||
data << "Content-Type: " << f.mimeType << "\r\n";
|
||||
|
||||
data << "Content-Transfer-Encoding: binary\r\n\r\n";
|
||||
|
||||
if (f.data != nullptr)
|
||||
data << *f.data;
|
||||
else
|
||||
data << f.file;
|
||||
|
||||
data << "\r\n--" << boundary;
|
||||
}
|
||||
|
||||
data << "--\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
data << URLHelpers::getMangledParameters (*this)
|
||||
<< postData;
|
||||
|
||||
// if the user-supplied headers didn't contain a content-type, add one now..
|
||||
if (! headers.containsIgnoreCase ("Content-Type"))
|
||||
headers << "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
|
||||
headers << "Content-length: " << (int) data.getDataSize() << "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::isProbablyAWebsiteURL (const String& possibleURL)
|
||||
{
|
||||
static const char* validProtocols[] = { "http:", "ftp:", "https:" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (validProtocols); ++i)
|
||||
if (possibleURL.startsWithIgnoreCase (validProtocols[i]))
|
||||
return true;
|
||||
|
||||
if (possibleURL.containsChar ('@')
|
||||
|| possibleURL.containsChar (' '))
|
||||
return false;
|
||||
|
||||
const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false)
|
||||
.fromLastOccurrenceOf (".", false, false));
|
||||
|
||||
return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
|
||||
}
|
||||
|
||||
bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
|
||||
{
|
||||
const int atSign = possibleEmailAddress.indexOfChar ('@');
|
||||
|
||||
return atSign > 0
|
||||
&& possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
|
||||
&& ! possibleEmailAddress.endsWithChar ('.');
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
InputStream* URL::createInputStream (const bool usePostCommand,
|
||||
OpenStreamProgressCallback* const progressCallback,
|
||||
void* const progressCallbackContext,
|
||||
String headers,
|
||||
const int timeOutMs,
|
||||
StringPairArray* const responseHeaders,
|
||||
int* statusCode) const
|
||||
{
|
||||
MemoryBlock headersAndPostData;
|
||||
|
||||
if (! headers.endsWithChar ('\n'))
|
||||
headers << "\r\n";
|
||||
|
||||
if (usePostCommand)
|
||||
createHeadersAndPostData (headers, headersAndPostData);
|
||||
|
||||
if (! headers.endsWithChar ('\n'))
|
||||
headers << "\r\n";
|
||||
|
||||
ScopedPointer<WebInputStream> wi (new WebInputStream (toString (! usePostCommand),
|
||||
usePostCommand, headersAndPostData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
if (statusCode != nullptr)
|
||||
*statusCode = wi->statusCode;
|
||||
|
||||
return wi->isError() ? nullptr : wi.release();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::readEntireBinaryStream (MemoryBlock& destData,
|
||||
const bool usePostCommand) const
|
||||
{
|
||||
const ScopedPointer<InputStream> in (createInputStream (usePostCommand));
|
||||
|
||||
if (in != nullptr)
|
||||
{
|
||||
in->readIntoMemoryBlock (destData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String URL::readEntireTextStream (const bool usePostCommand) const
|
||||
{
|
||||
const ScopedPointer<InputStream> in (createInputStream (usePostCommand));
|
||||
|
||||
if (in != nullptr)
|
||||
return in->readEntireStreamAsString();
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const
|
||||
{
|
||||
return XmlDocument::parse (readEntireTextStream (usePostCommand));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
URL URL::withParameter (const String& parameterName,
|
||||
const String& parameterValue) const
|
||||
{
|
||||
URL u (*this);
|
||||
u.addParameter (parameterName, parameterValue);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::withParameters (const StringPairArray& parametersToAdd) const
|
||||
{
|
||||
URL u (*this);
|
||||
|
||||
for (int i = 0; i < parametersToAdd.size(); ++i)
|
||||
u.addParameter (parametersToAdd.getAllKeys()[i],
|
||||
parametersToAdd.getAllValues()[i]);
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::withPOSTData (const String& newPostData) const
|
||||
{
|
||||
URL u (*this);
|
||||
u.postData = newPostData;
|
||||
return u;
|
||||
}
|
||||
|
||||
URL::Upload::Upload (const String& param, const String& name,
|
||||
const String& mime, const File& f, MemoryBlock* mb)
|
||||
: parameterName (param), filename (name), mimeType (mime), file (f), data (mb)
|
||||
{
|
||||
jassert (mimeType.isNotEmpty()); // You need to supply a mime type!
|
||||
}
|
||||
|
||||
URL URL::withUpload (Upload* const f) const
|
||||
{
|
||||
URL u (*this);
|
||||
|
||||
for (int i = u.filesToUpload.size(); --i >= 0;)
|
||||
if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName)
|
||||
u.filesToUpload.remove (i);
|
||||
|
||||
u.filesToUpload.add (f);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload,
|
||||
const String& mimeType) const
|
||||
{
|
||||
return withUpload (new Upload (parameterName, fileToUpload.getFileName(),
|
||||
mimeType, fileToUpload, nullptr));
|
||||
}
|
||||
|
||||
URL URL::withDataToUpload (const String& parameterName, const String& filename,
|
||||
const MemoryBlock& fileContentToUpload, const String& mimeType) const
|
||||
{
|
||||
return withUpload (new Upload (parameterName, filename, mimeType, File(),
|
||||
new MemoryBlock (fileContentToUpload)));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String URL::removeEscapeChars (const String& s)
|
||||
{
|
||||
String result (s.replaceCharacter ('+', ' '));
|
||||
|
||||
if (! result.containsChar ('%'))
|
||||
return result;
|
||||
|
||||
// We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
|
||||
// after all the replacements have been made, so that multi-byte chars are handled.
|
||||
Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
|
||||
|
||||
for (int i = 0; i < utf8.size(); ++i)
|
||||
{
|
||||
if (utf8.getUnchecked(i) == '%')
|
||||
{
|
||||
const int hexDigit1 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 1]);
|
||||
const int hexDigit2 = CharacterFunctions::getHexDigitValue ((juce_wchar) (uint8) utf8 [i + 2]);
|
||||
|
||||
if (hexDigit1 >= 0 && hexDigit2 >= 0)
|
||||
{
|
||||
utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
|
||||
utf8.removeRange (i + 1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
|
||||
}
|
||||
|
||||
String URL::addEscapeChars (const String& s, const bool isParameter)
|
||||
{
|
||||
const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()"
|
||||
: ",$_-.*!'()");
|
||||
|
||||
Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
|
||||
|
||||
for (int i = 0; i < utf8.size(); ++i)
|
||||
{
|
||||
const char c = utf8.getUnchecked(i);
|
||||
|
||||
if (! (CharacterFunctions::isLetterOrDigit (c)
|
||||
|| legalChars.indexOf ((juce_wchar) c) >= 0))
|
||||
{
|
||||
utf8.set (i, '%');
|
||||
utf8.insert (++i, "0123456789ABCDEF" [((uint8) c) >> 4]);
|
||||
utf8.insert (++i, "0123456789ABCDEF" [c & 15]);
|
||||
}
|
||||
}
|
||||
|
||||
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::launchInDefaultBrowser() const
|
||||
{
|
||||
String u (toString (true));
|
||||
|
||||
if (u.containsChar ('@') && ! u.containsChar (':'))
|
||||
u = "mailto:" + u;
|
||||
|
||||
return Process::openDocument (u, String());
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the juce_core module of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with
|
||||
or without fee is hereby granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
|
||||
NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
|
||||
All other JUCE modules are covered by a dual GPL/commercial license, so if you are
|
||||
using any other modules, be sure to check that you also comply with their license.
|
||||
|
||||
For more details, visit www.juce.com
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_URL_H_INCLUDED
|
||||
#define JUCE_URL_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a URL and has a bunch of useful functions to manipulate it.
|
||||
|
||||
This class can be used to launch URLs in browsers, and also to create
|
||||
InputStreams that can read from remote http or ftp sources.
|
||||
*/
|
||||
class JUCE_API URL
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty URL. */
|
||||
URL();
|
||||
|
||||
/** Creates a URL from a string.
|
||||
This will parse any embedded parameters after a '?' character and store them
|
||||
in the list (see getParameterNames etc). If you don't want this to happen, you
|
||||
can use createWithoutParsing().
|
||||
*/
|
||||
URL (const String& url);
|
||||
|
||||
/** Creates a copy of another URL. */
|
||||
URL (const URL& other);
|
||||
|
||||
/** Destructor. */
|
||||
~URL();
|
||||
|
||||
/** Copies this URL from another one. */
|
||||
URL& operator= (const URL& other);
|
||||
|
||||
/** Compares two URLs.
|
||||
All aspects of the URLs must be identical for them to match, including any parameters,
|
||||
upload files, etc.
|
||||
*/
|
||||
bool operator== (const URL&) const;
|
||||
bool operator!= (const URL&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string version of the URL.
|
||||
|
||||
If includeGetParameters is true and any parameters have been set with the
|
||||
withParameter() method, then the string will have these appended on the
|
||||
end and url-encoded.
|
||||
*/
|
||||
String toString (bool includeGetParameters) const;
|
||||
|
||||
/** Returns true if the URL is an empty string. */
|
||||
bool isEmpty() const noexcept;
|
||||
|
||||
/** True if it seems to be valid. */
|
||||
bool isWellFormed() const;
|
||||
|
||||
/** Returns just the domain part of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com".
|
||||
*/
|
||||
String getDomain() const;
|
||||
|
||||
/** Returns the path part of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar".
|
||||
*/
|
||||
String getSubPath() const;
|
||||
|
||||
/** Returns the scheme of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't
|
||||
include the colon).
|
||||
*/
|
||||
String getScheme() const;
|
||||
|
||||
/** Attempts to read a port number from the URL.
|
||||
@returns the port number, or 0 if none is explicitly specified.
|
||||
*/
|
||||
int getPort() const;
|
||||
|
||||
/** Returns a new version of this URL that uses a different sub-path.
|
||||
|
||||
E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with
|
||||
"bar", it'll return "http://www.xyz.com/bar?x=1".
|
||||
*/
|
||||
URL withNewSubPath (const String& newPath) const;
|
||||
|
||||
/** Returns a new URL that refers to a sub-path relative to this one.
|
||||
|
||||
E.g. if the URL is "http://www.xyz.com/foo" and you call this with
|
||||
"bar", it'll return "http://www.xyz.com/foo/bar". Note that there's no way for
|
||||
this method to know whether the original URL is a file or directory, so it's
|
||||
up to you to make sure it's a directory. It also won't attempt to be smart about
|
||||
the content of the childPath string, so if this string is an absolute URL, it'll
|
||||
still just get bolted onto the end of the path.
|
||||
|
||||
@see File::getChildFile
|
||||
*/
|
||||
URL getChildURL (const String& subPath) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a copy of this URL, with a GET or POST parameter added to the end.
|
||||
|
||||
Any control characters in the value will be encoded.
|
||||
|
||||
e.g. calling "withParameter ("amount", "some fish") for the url "www.fish.com"
|
||||
would produce a new url whose toString(true) method would return
|
||||
"www.fish.com?amount=some+fish".
|
||||
|
||||
@see getParameterNames, getParameterValues
|
||||
*/
|
||||
URL withParameter (const String& parameterName,
|
||||
const String& parameterValue) const;
|
||||
|
||||
/** Returns a copy of this URL, with a set of GET or POST parameters added.
|
||||
This is a convenience method, equivalent to calling withParameter for each value.
|
||||
@see withParameter
|
||||
*/
|
||||
URL withParameters (const StringPairArray& parametersToAdd) const;
|
||||
|
||||
/** Returns a copy of this URL, with a file-upload type parameter added to it.
|
||||
|
||||
When performing a POST where one of your parameters is a binary file, this
|
||||
lets you specify the file.
|
||||
|
||||
Note that the filename is stored, but the file itself won't actually be read
|
||||
until this URL is later used to create a network input stream. If you want to
|
||||
upload data from memory, use withDataToUpload().
|
||||
|
||||
@see withDataToUpload
|
||||
*/
|
||||
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.
|
||||
|
||||
When performing a POST where one of your parameters is a binary file, this
|
||||
lets you specify the file content.
|
||||
Note that the filename parameter should not be a full path, it's just the
|
||||
last part of the filename.
|
||||
|
||||
@see withFileToUpload
|
||||
*/
|
||||
URL withDataToUpload (const String& parameterName,
|
||||
const String& filename,
|
||||
const MemoryBlock& fileContentToUpload,
|
||||
const String& mimeType) const;
|
||||
|
||||
/** Returns an array of the names of all the URL's parameters.
|
||||
|
||||
E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would
|
||||
contain two items: "type" and "amount".
|
||||
|
||||
You can call getParameterValues() to get the corresponding value of each
|
||||
parameter. Note that the list can contain multiple parameters with the same name.
|
||||
|
||||
@see getParameterValues, withParameter
|
||||
*/
|
||||
const StringArray& getParameterNames() const noexcept { return parameterNames; }
|
||||
|
||||
/** Returns an array of the values of all the URL's parameters.
|
||||
|
||||
E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would
|
||||
contain two items: "haddock" and "some fish".
|
||||
|
||||
The values returned will have been cleaned up to remove any escape characters.
|
||||
|
||||
You can call getParameterNames() to get the corresponding name of each
|
||||
parameter. Note that the list can contain multiple parameters with the same name.
|
||||
|
||||
@see getParameterNames, withParameter
|
||||
*/
|
||||
const StringArray& getParameterValues() const noexcept { return parameterValues; }
|
||||
|
||||
/** Returns a copy of this URL, with a block of data to send as the POST data.
|
||||
|
||||
If you're setting the POST data, be careful not to have any parameters set
|
||||
as well, otherwise it'll all get thrown in together, and might not have the
|
||||
desired effect.
|
||||
|
||||
If the URL already contains some POST data, this will replace it, rather
|
||||
than being appended to it.
|
||||
|
||||
This data will only be used if you specify a post operation when you call
|
||||
createInputStream().
|
||||
*/
|
||||
URL withPOSTData (const String& postData) const;
|
||||
|
||||
/** Returns the data that was set using withPOSTData(). */
|
||||
const String& getPostData() const noexcept { return postData; }
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to launch the system's default browser to open the URL.
|
||||
|
||||
Returns true if this seems to have worked.
|
||||
*/
|
||||
bool launchInDefaultBrowser() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a guess as to whether a string might be a valid website address.
|
||||
|
||||
This isn't foolproof!
|
||||
*/
|
||||
static bool isProbablyAWebsiteURL (const String& possibleURL);
|
||||
|
||||
/** Takes a guess as to whether a string might be a valid email address.
|
||||
|
||||
This isn't foolproof!
|
||||
*/
|
||||
static bool isProbablyAnEmailAddress (const String& possibleEmailAddress);
|
||||
|
||||
//==============================================================================
|
||||
/** This callback function can be used by the createInputStream() method.
|
||||
|
||||
It allows your app to receive progress updates during a lengthy POST operation. If you
|
||||
want to continue the operation, this should return true, or false to abort.
|
||||
*/
|
||||
typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes);
|
||||
|
||||
/** Attempts to open a stream that can read from this URL.
|
||||
|
||||
@param usePostCommand if true, it will try to do use a http 'POST' to pass
|
||||
the parameters, otherwise it'll encode them into the
|
||||
URL and do a 'GET'.
|
||||
@param progressCallback if this is non-zero, it lets you supply a callback function
|
||||
to keep track of the operation's progress. This can be useful
|
||||
for lengthy POST operations, so that you can provide user feedback.
|
||||
@param progressCallbackContext if a callback is specified, this value will be passed to
|
||||
the function
|
||||
@param extraHeaders if not empty, this string is appended onto the headers that
|
||||
are used for the request. It must therefore be a valid set of HTML
|
||||
header directives, separated by newlines.
|
||||
@param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If
|
||||
a negative number, it will be infinite. Otherwise it specifies a
|
||||
time in milliseconds.
|
||||
@param responseHeaders if this is non-null, all the (key, value) pairs received as headers
|
||||
in the response will be stored in this array
|
||||
@param statusCode if this is non-null, it will get set to the http status code, if one
|
||||
is known, or 0 if a code isn't available
|
||||
@returns an input stream that the caller must delete, or a null pointer if there was an
|
||||
error trying to open it.
|
||||
*/
|
||||
InputStream* createInputStream (bool usePostCommand,
|
||||
OpenStreamProgressCallback* progressCallback = nullptr,
|
||||
void* progressCallbackContext = nullptr,
|
||||
String extraHeaders = String(),
|
||||
int connectionTimeOutMs = 0,
|
||||
StringPairArray* responseHeaders = nullptr,
|
||||
int* statusCode = nullptr) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to download the entire contents of this URL into a binary data block.
|
||||
|
||||
If it succeeds, this will return true and append the data it read onto the end
|
||||
of the memory block.
|
||||
|
||||
@param destData the memory block to append the new data to
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
@see readEntireTextStream, readEntireXmlStream
|
||||
*/
|
||||
bool readEntireBinaryStream (MemoryBlock& destData,
|
||||
bool usePostCommand = false) const;
|
||||
|
||||
/** Tries to download the entire contents of this URL as a string.
|
||||
|
||||
If it fails, this will return an empty string, otherwise it will return the
|
||||
contents of the downloaded file. If you need to distinguish between a read
|
||||
operation that fails and one that returns an empty string, you'll need to use
|
||||
a different method, such as readEntireBinaryStream().
|
||||
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
@see readEntireBinaryStream, readEntireXmlStream
|
||||
*/
|
||||
String readEntireTextStream (bool usePostCommand = false) const;
|
||||
|
||||
/** Tries to download the entire contents of this URL and parse it as XML.
|
||||
|
||||
If it fails, or if the text that it reads can't be parsed as XML, this will
|
||||
return 0.
|
||||
|
||||
When it returns a valid XmlElement object, the caller is responsibile for deleting
|
||||
this object when no longer needed.
|
||||
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
|
||||
@see readEntireBinaryStream, readEntireTextStream
|
||||
*/
|
||||
XmlElement* readEntireXmlStream (bool usePostCommand = false) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds escape sequences to a string to encode any characters that aren't
|
||||
legal in a URL.
|
||||
|
||||
E.g. any spaces will be replaced with "%20".
|
||||
|
||||
This is the opposite of removeEscapeChars().
|
||||
|
||||
If isParameter is true, it means that the string is going to be used
|
||||
as a parameter, so it also encodes '$' and ',' (which would otherwise
|
||||
be legal in a URL.
|
||||
|
||||
@see removeEscapeChars
|
||||
*/
|
||||
static String addEscapeChars (const String& stringToAddEscapeCharsTo,
|
||||
bool isParameter);
|
||||
|
||||
/** Replaces any escape character sequences in a string with their original
|
||||
character codes.
|
||||
|
||||
E.g. any instances of "%20" will be replaced by a space.
|
||||
|
||||
This is the opposite of addEscapeChars().
|
||||
|
||||
@see addEscapeChars
|
||||
*/
|
||||
static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom);
|
||||
|
||||
/** Returns a URL without attempting to remove any embedded parameters from the string.
|
||||
This may be necessary if you need to create a request that involves both POST
|
||||
parameters and parameters which are embedded in the URL address itself.
|
||||
*/
|
||||
static URL createWithoutParsing (const String& url);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String url, postData;
|
||||
StringArray parameterNames, parameterValues;
|
||||
|
||||
struct Upload : public ReferenceCountedObject
|
||||
{
|
||||
Upload (const String&, const String&, const String&, const File&, MemoryBlock*);
|
||||
String parameterName, filename, mimeType;
|
||||
File file;
|
||||
ScopedPointer<MemoryBlock> data;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload)
|
||||
};
|
||||
|
||||
friend struct ContainerDeletePolicy<Upload>;
|
||||
ReferenceCountedArray<Upload> filesToUpload;
|
||||
|
||||
URL (const String&, int);
|
||||
void addParameter (const String&, const String&);
|
||||
void createHeadersAndPostData (String&, MemoryBlock&) const;
|
||||
URL withUpload (Upload*) const;
|
||||
|
||||
JUCE_LEAK_DETECTOR (URL)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_URL_H_INCLUDED
|
||||
Reference in New Issue
Block a user