mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-05 03:50:07 +00:00
499 lines
17 KiB
C++
499 lines
17 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-11 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// (This file gets included by juce_win32_NativeCode.cpp, rather than being
|
|
// compiled on its own).
|
|
#if JUCE_INCLUDED_FILE
|
|
|
|
|
|
#ifndef INTERNET_FLAG_NEED_FILE
|
|
#define INTERNET_FLAG_NEED_FILE 0x00000010
|
|
#endif
|
|
|
|
#ifndef INTERNET_OPTION_DISABLE_AUTODIAL
|
|
#define INTERNET_OPTION_DISABLE_AUTODIAL 70
|
|
#endif
|
|
|
|
//==============================================================================
|
|
#ifndef WORKAROUND_TIMEOUT_BUG
|
|
//#define WORKAROUND_TIMEOUT_BUG 1
|
|
#endif
|
|
|
|
#if WORKAROUND_TIMEOUT_BUG
|
|
// Required because of a Microsoft bug in setting a timeout
|
|
class InternetConnectThread : public Thread
|
|
{
|
|
public:
|
|
InternetConnectThread (URL_COMPONENTS& uc_, HINTERNET sessionHandle_, HINTERNET& connection_, const bool isFtp_)
|
|
: Thread ("Internet"), uc (uc_), sessionHandle (sessionHandle_), connection (connection_), isFtp (isFtp_)
|
|
{
|
|
startThread();
|
|
}
|
|
|
|
~InternetConnectThread()
|
|
{
|
|
stopThread (60000);
|
|
}
|
|
|
|
void run()
|
|
{
|
|
connection = InternetConnect (sessionHandle, uc.lpszHostName,
|
|
uc.nPort, _T(""), _T(""),
|
|
isFtp ? INTERNET_SERVICE_FTP
|
|
: INTERNET_SERVICE_HTTP,
|
|
0, 0);
|
|
notify();
|
|
}
|
|
|
|
private:
|
|
URL_COMPONENTS& uc;
|
|
HINTERNET sessionHandle;
|
|
HINTERNET& connection;
|
|
const bool isFtp;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternetConnectThread);
|
|
};
|
|
#endif
|
|
|
|
|
|
//==============================================================================
|
|
class WebInputStream : public InputStream
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
|
|
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
|
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
|
|
: connection (0), request (0),
|
|
address (address_), headers (headers_), postData (postData_), position (0),
|
|
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
|
|
{
|
|
createConnection (progressCallback, progressCallbackContext);
|
|
|
|
if (responseHeaders != nullptr && ! isError())
|
|
{
|
|
DWORD bufferSizeBytes = 4096;
|
|
|
|
for (;;)
|
|
{
|
|
HeapBlock<char> buffer ((size_t) bufferSizeBytes);
|
|
|
|
if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0))
|
|
{
|
|
StringArray headersArray;
|
|
headersArray.addLines (reinterpret_cast <const WCHAR*> (buffer.getData()));
|
|
|
|
for (int i = 0; i < headersArray.size(); ++i)
|
|
{
|
|
const String& header = headersArray[i];
|
|
const String key (header.upToFirstOccurrenceOf (": ", false, false));
|
|
const String value (header.fromFirstOccurrenceOf (": ", false, false));
|
|
const String previousValue ((*responseHeaders) [key]);
|
|
|
|
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
~WebInputStream()
|
|
{
|
|
close();
|
|
}
|
|
|
|
//==============================================================================
|
|
bool isError() const { return request == 0; }
|
|
bool isExhausted() { return finished; }
|
|
int64 getPosition() { return position; }
|
|
|
|
int64 getTotalLength()
|
|
{
|
|
if (! isError())
|
|
{
|
|
DWORD index = 0, result = 0, size = sizeof (result);
|
|
|
|
if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index))
|
|
return (int64) result;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int read (void* buffer, int bytesToRead)
|
|
{
|
|
DWORD bytesRead = 0;
|
|
|
|
if (! (finished || isError()))
|
|
{
|
|
InternetReadFile (request, buffer, bytesToRead, &bytesRead);
|
|
position += bytesRead;
|
|
|
|
if (bytesRead == 0)
|
|
finished = true;
|
|
}
|
|
|
|
return (int) bytesRead;
|
|
}
|
|
|
|
bool setPosition (int64 wantedPos)
|
|
{
|
|
if (isError())
|
|
return false;
|
|
|
|
if (wantedPos != position)
|
|
{
|
|
finished = false;
|
|
position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0);
|
|
|
|
if (position == wantedPos)
|
|
return true;
|
|
|
|
if (wantedPos < position)
|
|
{
|
|
close();
|
|
position = 0;
|
|
createConnection (0, 0);
|
|
}
|
|
|
|
skipNextBytes (wantedPos - position);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
//==============================================================================
|
|
HINTERNET connection, request;
|
|
String address, headers;
|
|
MemoryBlock postData;
|
|
int64 position;
|
|
bool finished;
|
|
const bool isPost;
|
|
int timeOutMs;
|
|
|
|
void close()
|
|
{
|
|
if (request != 0)
|
|
{
|
|
InternetCloseHandle (request);
|
|
request = 0;
|
|
}
|
|
|
|
if (connection != 0)
|
|
{
|
|
InternetCloseHandle (connection);
|
|
connection = 0;
|
|
}
|
|
}
|
|
|
|
void createConnection (URL::OpenStreamProgressCallback* progressCallback,
|
|
void* progressCallbackContext)
|
|
{
|
|
static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
|
|
|
|
close();
|
|
|
|
if (sessionHandle != 0)
|
|
{
|
|
// break up the url..
|
|
const int fileNumChars = 65536;
|
|
const int serverNumChars = 2048;
|
|
const int usernameNumChars = 1024;
|
|
const int passwordNumChars = 1024;
|
|
HeapBlock<TCHAR> file (fileNumChars), server (serverNumChars),
|
|
username (usernameNumChars), password (passwordNumChars);
|
|
|
|
URL_COMPONENTS uc = { 0 };
|
|
uc.dwStructSize = sizeof (uc);
|
|
uc.lpszUrlPath = file;
|
|
uc.dwUrlPathLength = fileNumChars;
|
|
uc.lpszHostName = server;
|
|
uc.dwHostNameLength = serverNumChars;
|
|
uc.lpszUserName = username;
|
|
uc.dwUserNameLength = usernameNumChars;
|
|
uc.lpszPassword = password;
|
|
uc.dwPasswordLength = passwordNumChars;
|
|
|
|
if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc))
|
|
openConnection (uc, sessionHandle, progressCallback, progressCallbackContext);
|
|
}
|
|
}
|
|
|
|
void openConnection (URL_COMPONENTS& uc, HINTERNET sessionHandle,
|
|
URL::OpenStreamProgressCallback* progressCallback,
|
|
void* progressCallbackContext)
|
|
{
|
|
int disable = 1;
|
|
InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable));
|
|
|
|
if (timeOutMs == 0)
|
|
timeOutMs = 30000;
|
|
else if (timeOutMs < 0)
|
|
timeOutMs = -1;
|
|
|
|
InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs));
|
|
|
|
const bool isFtp = address.startsWithIgnoreCase ("ftp:");
|
|
|
|
#if WORKAROUND_TIMEOUT_BUG
|
|
connection = 0;
|
|
|
|
{
|
|
InternetConnectThread connectThread (uc, sessionHandle, connection, isFtp);
|
|
connectThread.wait (timeOutMs);
|
|
|
|
if (connection == 0)
|
|
{
|
|
InternetCloseHandle (sessionHandle);
|
|
sessionHandle = 0;
|
|
}
|
|
}
|
|
#else
|
|
connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort,
|
|
uc.lpszUserName, uc.lpszPassword,
|
|
isFtp ? INTERNET_SERVICE_FTP
|
|
: INTERNET_SERVICE_HTTP,
|
|
0, 0);
|
|
#endif
|
|
|
|
if (connection != 0)
|
|
{
|
|
if (isFtp)
|
|
request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ,
|
|
FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0);
|
|
else
|
|
openHTTPConnection (uc, progressCallback, progressCallbackContext);
|
|
}
|
|
}
|
|
|
|
void openHTTPConnection (URL_COMPONENTS& uc, URL::OpenStreamProgressCallback* progressCallback,
|
|
void* progressCallbackContext)
|
|
{
|
|
const TCHAR* mimeTypes[] = { _T("*/*"), 0 };
|
|
|
|
DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES;
|
|
|
|
if (address.startsWithIgnoreCase ("https:"))
|
|
flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 -
|
|
// IE7 seems to automatically work out when it's https)
|
|
|
|
request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"),
|
|
uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0);
|
|
|
|
if (request != 0)
|
|
{
|
|
INTERNET_BUFFERS buffers = { 0 };
|
|
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
|
|
buffers.lpcszHeader = headers.toWideCharPointer();
|
|
buffers.dwHeadersLength = headers.length();
|
|
buffers.dwBufferTotal = (DWORD) postData.getSize();
|
|
|
|
if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
|
|
{
|
|
int bytesSent = 0;
|
|
|
|
for (;;)
|
|
{
|
|
const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent);
|
|
DWORD bytesDone = 0;
|
|
|
|
if (bytesToDo > 0
|
|
&& ! InternetWriteFile (request,
|
|
static_cast <const char*> (postData.getData()) + bytesSent,
|
|
bytesToDo, &bytesDone))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (bytesToDo == 0 || (int) bytesDone < bytesToDo)
|
|
{
|
|
if (HttpEndRequest (request, 0, 0, 0))
|
|
return;
|
|
|
|
break;
|
|
}
|
|
|
|
bytesSent += bytesDone;
|
|
|
|
if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, bytesSent, postData.getSize()))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
close();
|
|
}
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream);
|
|
};
|
|
|
|
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
|
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
|
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
|
{
|
|
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
|
progressCallback, progressCallbackContext,
|
|
headers, timeOutMs, responseHeaders));
|
|
|
|
return wi->isError() ? nullptr : wi.release();
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
namespace MACAddressHelpers
|
|
{
|
|
void getViaGetAdaptersInfo (Array<MACAddress>& result)
|
|
{
|
|
DynamicLibrary dll ("iphlpapi.dll");
|
|
JUCE_DLL_FUNCTION (GetAdaptersInfo, getAdaptersInfo, DWORD, dll, (PIP_ADAPTER_INFO, PULONG))
|
|
|
|
if (getAdaptersInfo != 0)
|
|
{
|
|
ULONG len = sizeof (IP_ADAPTER_INFO);
|
|
MemoryBlock mb;
|
|
PIP_ADAPTER_INFO adapterInfo = (PIP_ADAPTER_INFO) mb.getData();
|
|
|
|
if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
mb.setSize (len);
|
|
adapterInfo = (PIP_ADAPTER_INFO) mb.getData();
|
|
}
|
|
|
|
if (getAdaptersInfo (adapterInfo, &len) == NO_ERROR)
|
|
{
|
|
for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != 0; adapter = adapter->Next)
|
|
{
|
|
if (adapter->AddressLength >= 6)
|
|
result.addIfNotAlreadyThere (MACAddress (adapter->Address));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void getViaNetBios (Array<MACAddress>& result)
|
|
{
|
|
DynamicLibrary dll ("netapi32.dll");
|
|
JUCE_DLL_FUNCTION (Netbios, NetbiosCall, UCHAR, dll, (PNCB))
|
|
|
|
if (NetbiosCall != 0)
|
|
{
|
|
LANA_ENUM enums = { 0 };
|
|
|
|
{
|
|
NCB ncb = { 0 };
|
|
ncb.ncb_command = NCBENUM;
|
|
ncb.ncb_buffer = (unsigned char*) &enums;
|
|
ncb.ncb_length = sizeof (LANA_ENUM);
|
|
NetbiosCall (&ncb);
|
|
}
|
|
|
|
for (int i = 0; i < enums.length; ++i)
|
|
{
|
|
NCB ncb2 = { 0 };
|
|
ncb2.ncb_command = NCBRESET;
|
|
ncb2.ncb_lana_num = enums.lana[i];
|
|
|
|
if (NetbiosCall (&ncb2) == 0)
|
|
{
|
|
NCB ncb = { 0 };
|
|
memcpy (ncb.ncb_callname, "* ", NCBNAMSZ);
|
|
ncb.ncb_command = NCBASTAT;
|
|
ncb.ncb_lana_num = enums.lana[i];
|
|
|
|
struct ASTAT
|
|
{
|
|
ADAPTER_STATUS adapt;
|
|
NAME_BUFFER NameBuff [30];
|
|
};
|
|
|
|
ASTAT astat = { 0 };
|
|
ncb.ncb_buffer = (unsigned char*) &astat;
|
|
ncb.ncb_length = sizeof (ASTAT);
|
|
|
|
if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe)
|
|
result.addIfNotAlreadyThere (MACAddress (astat.adapt.adapter_address));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
|
{
|
|
MACAddressHelpers::getViaGetAdaptersInfo (result);
|
|
MACAddressHelpers::getViaNetBios (result);
|
|
}
|
|
|
|
//==============================================================================
|
|
bool Process::openEmailWithAttachments (const String& targetEmailAddress,
|
|
const String& emailSubject,
|
|
const String& bodyText,
|
|
const StringArray& filesToAttach)
|
|
{
|
|
typedef ULONG (WINAPI *MAPISendMailType) (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG);
|
|
|
|
DynamicLibrary mapiLib ("MAPI32.dll");
|
|
MAPISendMailType mapiSendMail = (MAPISendMailType) mapiLib.getFunction ("MAPISendMail");
|
|
|
|
if (mapiSendMail == nullptr)
|
|
return false;
|
|
|
|
MapiMessage message = { 0 };
|
|
message.lpszSubject = (LPSTR) emailSubject.toUTF8().getAddress();
|
|
message.lpszNoteText = (LPSTR) bodyText.toUTF8().getAddress();
|
|
|
|
MapiRecipDesc recip = { 0 };
|
|
recip.ulRecipClass = MAPI_TO;
|
|
String targetEmailAddress_ (targetEmailAddress);
|
|
if (targetEmailAddress_.isEmpty())
|
|
targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address)
|
|
recip.lpszName = (LPSTR) targetEmailAddress_.toUTF8().getAddress();
|
|
message.nRecipCount = 1;
|
|
message.lpRecips = &recip;
|
|
|
|
HeapBlock <MapiFileDesc> files;
|
|
files.calloc (filesToAttach.size());
|
|
|
|
message.nFileCount = filesToAttach.size();
|
|
message.lpFiles = files;
|
|
|
|
for (int i = 0; i < filesToAttach.size(); ++i)
|
|
{
|
|
files[i].nPosition = (ULONG) -1;
|
|
files[i].lpszPathName = (LPSTR) filesToAttach[i].toUTF8().getAddress();
|
|
}
|
|
|
|
return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS;
|
|
}
|
|
|
|
|
|
#endif
|