home *** CD-ROM | disk | FTP | other *** search
- // ===========================================================================
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
- // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- // PARTICULAR PURPOSE.
- //
- // Copyright 1996 Microsoft Corporation. All Rights Reserved.
- // ===========================================================================
- #include <urlmon.h>
- #include <wininet.h>
- #include "callback.hpp"
-
- #define BOUNDARY_MAXSIZE 500
- #define RECV_BUF_SIZE 8192
-
- static char szCRLF[] = "\r\n";
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::CUrlmonCallback
- // ---------------------------------------------------------------------------
- CUrlmonCallback::CUrlmonCallback (PHTTP_REQUEST_PARAM pParam)
- {
- m_pBinding = NULL;
- m_pstm = NULL;
- m_cRef = 0;
-
- m_pParam = pParam;
-
- m_CBParam.cbStruct = sizeof(m_CBParam);
- m_CBParam.dwRequestCtx = m_pParam->dwRequestCtx;
-
- m_dwOffset = 0;
- m_dwResponseCode = 0;
- m_pszRangeDelimiter = NULL;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::~CUrlmonCallback
- // ---------------------------------------------------------------------------
- CUrlmonCallback::~CUrlmonCallback()
- {
- if (m_pstm)
- m_pstm->Release();
- if (m_pBinding)
- m_pBinding->Release();
- if (m_pszRangeDelimiter)
- LocalFree ((HLOCAL) m_pszRangeDelimiter);
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::QueryInterface
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::QueryInterface(REFIID riid, void** ppv)
- {
- if(IsEqualGUID(riid,IID_IUnknown))
- *ppv = (IUnknown *) (IBindStatusCallback *) this;
- else if (IsEqualGUID(riid,IID_IBindStatusCallback))
- *ppv = (IBindStatusCallback *) this;
- else if (IsEqualGUID(riid, IID_IHttpNegotiate))
- *ppv = (IHttpNegotiate *) this;
- else
- *ppv = NULL;
-
- if (!*ppv)
- return E_NOINTERFACE;
-
- // Increment our reference count before we hand out our interface
- ((LPUNKNOWN)*ppv)->AddRef();
- return S_OK;
-
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::BeginningTransaction
- // ---------------------------------------------------------------------------
- STDMETHODIMP
- CUrlmonCallback::BeginningTransaction (LPCWSTR szURL,
- LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *ppszAdditionalHeaders)
- {
- static char szRangeHeader[] = "Range: bytes=";
- static char szUnlessHeader[] = "Unless-Modified-Since: ";
-
- HRESULT hr = S_OK;
- PSTR pszHeader = NULL;
-
- // Don't add any headers if not a range request.
- DWORD cRanges = m_pParam->cRanges;
- if (!cRanges)
- {
- *ppszAdditionalHeaders = NULL;
- goto done;
- }
-
- PHTTP_REQUEST_RANGE pRanges;
- pRanges = m_pParam->pRanges;
-
- // Allocate ANSI buffer.
- DWORD cbHeader;
- cbHeader = sizeof(szRangeHeader) + 20 * cRanges + 2;
- if (m_pParam->pstUnlessModifiedSince)
- cbHeader += sizeof(szUnlessHeader) + INTERNET_RFC1123_BUFSIZE + 2;
- pszHeader = (PSTR) LocalAlloc (LMEM_FIXED, cbHeader);
- if (!pszHeader)
- {
- hr = E_OUTOFMEMORY;
- goto done;
- }
-
- // Format the read range request header.
- UINT cchHeader;
- cchHeader = wsprintf (pszHeader, "%s", szRangeHeader);
-
- // Add the ranges.
- while (cRanges--)
- {
- if (pRanges->dwSize)
- {
- // Format range, end value is inclusive.
- cchHeader += wsprintf (pszHeader + cchHeader, "%d-%d",
- pRanges->dwOffset, pRanges->dwOffset + pRanges->dwSize - 1);
- }
- else
- {
- // Format range to end of file.
- cchHeader += wsprintf (pszHeader + cchHeader, "%d-",
- pRanges->dwOffset);
- }
-
- pRanges++;
- if (cRanges)
- cchHeader += wsprintf (pszHeader + cchHeader, ", ");
- else
- cchHeader += wsprintf (pszHeader + cchHeader, szCRLF);
- }
-
- if (m_pParam->pstUnlessModifiedSince)
- {
- // Add unless-modified-since qualifier.
- cchHeader += wsprintf (pszHeader + cchHeader, szUnlessHeader);
- if (!InternetTimeFromSystemTime
- (
- m_pParam->pstUnlessModifiedSince,
- INTERNET_RFC1123_FORMAT,
- pszHeader + cchHeader,
- INTERNET_RFC1123_BUFSIZE
- ))
- {
- hr = E_FAIL;
- goto done;
- }
-
- cchHeader += lstrlen (pszHeader + cchHeader);
- cchHeader += wsprintf (pszHeader + cchHeader, szCRLF);
- }
-
- cchHeader++; // for NULL termination
-
- // Allocate Unicode buffer.
- *ppszAdditionalHeaders = (WCHAR *) CoTaskMemAlloc (sizeof(WCHAR) * cchHeader);
- if (*ppszAdditionalHeaders)
- {
- MultiByteToWideChar (CP_ACP, 0, pszHeader, -1, *ppszAdditionalHeaders,
- sizeof(WCHAR) * cchHeader);
- }
-
- hr = *ppszAdditionalHeaders? S_OK : E_OUTOFMEMORY;
-
- done:
- if (pszHeader)
- LocalFree (pszHeader);
- return hr;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnResponse
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnResponse
- (
- DWORD dwResponseCode,
- LPCWSTR szResponseHeaders,
- LPCWSTR szRequestHeaders,
- LPWSTR *pszAdditionalRequestHeaders
- )
- {
- // Get the HttpQueryInfo wrapper object.
- IWinInetHttpInfo *pHttpInfo;
- HRESULT hr = m_pBinding->QueryInterface
- (IID_IWinInetHttpInfo, (void **) &pHttpInfo);
- if (FAILED(hr))
- return E_FAIL;
-
- // Save the response code.
- m_dwResponseCode = dwResponseCode;
- m_CBParam.dwResponseCode = dwResponseCode;
-
- DWORD cbBuf;
-
- // Check for partial response.
- if (dwResponseCode == 206)
- {
- // Server responded with byte ranges, ergo must support them.
- m_CBParam.fdwRequestFlags = HTTP_REQUEST_ACCEPT_RANGES;
-
- // Look for multi-part delimiter embedded in content type.
- const static char szMultiPart[] = "multipart/x-byteranges; boundary";
- const static DWORD cbMultiPart = sizeof(szMultiPart);
-
- // Get length of buffer to hold content type.
- DWORD cbContentType = 0;
- pHttpInfo->QueryInfo
- (HTTP_QUERY_CONTENT_TYPE, NULL, &cbContentType, NULL, 0);
-
- if (cbContentType > cbMultiPart + 1)
- {
- // Content type could be big enough to embed a delimiter.
- m_pszRangeDelimiter = (PSTR) LocalAlloc (LMEM_FIXED, cbContentType);
- if (!m_pszRangeDelimiter)
- return E_OUTOFMEMORY;
-
- if (S_OK != pHttpInfo->QueryInfo (HTTP_QUERY_CONTENT_TYPE,
- m_pszRangeDelimiter, &cbContentType, NULL, 0))
- {
- return E_FAIL;
- }
-
- // Split the string at the '='
- m_pszRangeDelimiter[cbMultiPart - 1] = 0;
- if (lstrcmpi (m_pszRangeDelimiter, szMultiPart))
- {
- // Response must not be multi-part.
- LocalFree ((HLOCAL) m_pszRangeDelimiter);
- m_pszRangeDelimiter = NULL;
- }
- else
- {
- m_cchRangeDelimiter =
- lstrlen (m_pszRangeDelimiter + cbMultiPart);
-
- // Shift the delimiter to offset 2 of string.
- MoveMemory
- (
- m_pszRangeDelimiter + 2, // +2 for prefix
- m_pszRangeDelimiter + cbMultiPart,
- m_cchRangeDelimiter + 1 // +1 for null
- );
-
- // Prefix delimiter with "--"
- m_pszRangeDelimiter[0] = '-';
- m_pszRangeDelimiter[1] = '-';
- m_cchRangeDelimiter += 2;
-
- // Initialize range boundaries.
- m_dwRangeBeg = 0;
- m_dwRangeEnd = 0;
- }
-
- } // end if (cbContentType > cbMultiPart + 1)
-
- // Adjust the offset if we have a single range
- if (!m_pszRangeDelimiter)
- m_dwOffset = m_pParam->pRanges[0].dwOffset;
- }
-
- else // if (dwResponseCode != 206)
- {
- // Check if ranges are supported.
- static char szBytes[] = "bytes";
- char szBuf[sizeof(szBytes)];
- cbBuf = sizeof(szBytes);
- hr = pHttpInfo->QueryInfo
- (HTTP_QUERY_ACCEPT_RANGES, szBuf, &cbBuf, NULL, 0);
- if (SUCCEEDED(hr) && !lstrcmpi (szBytes, szBuf))
- m_CBParam.fdwRequestFlags = HTTP_REQUEST_ACCEPT_RANGES;
- else
- m_CBParam.fdwRequestFlags = 0;
- }
-
- // Get last modified time.
- SYSTEMTIME stLastModified;
- cbBuf = sizeof(stLastModified);
- hr = pHttpInfo->QueryInfo
- (
- HTTP_QUERY_FLAG_SYSTEMTIME | HTTP_QUERY_LAST_MODIFIED,
- &stLastModified, &cbBuf, NULL, 0
- );
- m_CBParam.pstLastModified = hr==S_OK? &stLastModified : NULL;
-
- // Get content length.
- m_CBParam.dwContentLength = 0;
- if (dwResponseCode != 206)
- {
- cbBuf = sizeof(m_CBParam.dwContentLength);
- hr = pHttpInfo->QueryInfo
- (
- HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH,
- &m_CBParam.dwContentLength, &cbBuf, NULL, 0
- );
- }
-
- pHttpInfo->Release;
-
- // Inform the client the request is started.
- m_CBParam.CallbackType = REQUESTCB_STARTED;
- if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
- {
- // Client wants to stop.
- m_pBinding->Abort();
- return S_OK;
- }
-
- return S_OK;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnStartBinding
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnStartBinding
- (DWORD grfBSCOption, IBinding* pBinding)
- {
- if (pBinding != NULL)
- {
- m_pBinding = pBinding;
- m_pBinding->AddRef();
- }
-
- // Initialize receive buffer.
- if (!BufAlloc(RECV_BUF_SIZE))
- return E_OUTOFMEMORY;
-
- return S_OK;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::GetPriority
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::GetPriority(LONG* pnPriority)
- {
- return E_NOTIMPL;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnLowResource
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnLowResource(DWORD dwReserved)
- {
- return E_NOTIMPL;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnProgress
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnProgress
- (ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
- {
- return S_OK;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnStopBinding
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnStopBinding
- (HRESULT hrStatus, LPCWSTR pszError)
- {
- // Release the binding, which will eventually release the callback object.
- m_pBinding->Release();
- m_pBinding = NULL;
-
- // Notify the client that we are done.
- m_CBParam.CallbackType = REQUESTCB_DONE;
- m_CBParam.hrRequest = hrStatus;
- (*(m_pParam->pfnRequestCB)) (&m_CBParam);
-
- return S_OK;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::GetBindInfo
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::GetBindInfo
- (DWORD* pgrfBINDF, BINDINFO* pbindInfo)
- {
- *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
- *pgrfBINDF |= BINDF_GETNEWESTVERSION;
- pbindInfo->cbSize = sizeof(BINDINFO);
- pbindInfo->szExtraInfo = NULL;
- ZeroMemory (&pbindInfo->stgmedData, sizeof(STGMEDIUM));
- pbindInfo->grfBindInfoF = 0;
- pbindInfo->dwBindVerb = BINDVERB_GET;
- pbindInfo->szCustomVerb = NULL;
- return S_OK;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnDataAvailable
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnDataAvailable
- (DWORD grfBSCF, DWORD dwSize, FORMATETC* pfmtetc, STGMEDIUM* pstgmed)
- {
- if (!m_pstm && pstgmed->tymed == TYMED_ISTREAM)
- {
- m_pstm = pstgmed->pstm;
- m_pstm->AddRef();
- m_CBParam.CallbackType = REQUESTCB_DATA;
- }
-
- // Check for multi-part response.
- if (m_pszRangeDelimiter)
- return ParseMultiPartBuffer (grfBSCF & BSCF_LASTDATANOTIFICATION);
-
- HRESULT hrRead;
- DWORD cbActual;
-
- do // until hrRead != S_OK
- {
- // Read some more data.
- hrRead = m_pstm->Read (BufBeg(), BufSize(), &cbActual);
-
- // Notify the client we got some data.
- if (cbActual)
- {
- m_CBParam.dwDataOffset = m_dwOffset;
- m_CBParam.lpDataBuffer = BufBeg();
- m_CBParam.cbDataLength = cbActual;
- if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
- {
- // Client wants to stop.
- m_pBinding->Abort();
- return S_OK;
- }
- }
-
- m_dwOffset += cbActual;
-
- } while (hrRead == S_OK);
-
- return (hrRead == S_FALSE || hrRead == E_PENDING)? S_OK : hrRead;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::OnObjectAvailable
- // ---------------------------------------------------------------------------
- STDMETHODIMP CUrlmonCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
- {
- return E_NOTIMPL;
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::ParseMultiPartHeader
- // ---------------------------------------------------------------------------
- BOOL CUrlmonCallback::ParseMultiPartHeader (void)
- {
- // multi-part boundary string literals.
- static char szLF[] = "\n";
- static char szContentType[] = "Content-Type: ";
- static char szContentRange[] = "Content-Range: bytes ";
-
- // Some macros for prettiness...
- #define GrokStr(str) if (!BufScanStr (str, sizeof(str)-1)) return FALSE;
- #define GrokInt(pint, delim) if (!BufScanInt (pint,delim)) return FALSE;
-
- // Check for presence of boundary.
- if (m_dwRangeBeg > 0) // must not be first range
- GrokStr (szLF); // will also detect a CR-LF
- if (!BufScanStr (m_pszRangeDelimiter, m_cchRangeDelimiter))
- return FALSE;
-
- // Check for end boundary.
- if
- ( m_pbDataBeg + 2 <= m_pbDataEnd
- && m_pbDataBeg[0] == '-'
- && m_pbDataBeg[1] == '-'
- )
- {
- m_dwRangeBeg = 0;
- m_dwRangeEnd = 0;
- return TRUE;
- }
-
- // Scan content type and data range.
- GrokStr (szLF);
- GrokStr (szContentType);
- GrokStr (szLF);
- GrokStr (szContentRange);
- GrokInt (&m_dwRangeBeg, '-');
- GrokInt (&m_dwRangeEnd, '/');
- GrokStr (szLF);
- GrokStr (szLF);
-
- // Range end is inclusive; make it exclusive.
- m_dwRangeEnd++;
- return TRUE;
-
- #undef GrokStr
- #undef GrokInt
- }
-
- // ---------------------------------------------------------------------------
- // CUrlmonCallback::ParseMultiPartBuffer
- // ---------------------------------------------------------------------------
- HRESULT CUrlmonCallback::ParseMultiPartBuffer (BOOL fLastCall)
- {
- HRESULT hrRead;
- DWORD cbActual;
-
- do // until hrRead != S_OK
- {
- // Read data and append to buffer.
- hrRead = m_pstm->Read(m_pbDataEnd, BufSpace(), &cbActual);
- m_pbDataEnd += cbActual;
-
- // Dispatch to current state handler.
- if (m_dwRangeBeg != m_dwRangeEnd)
- goto process_data;
- else
- goto parse_header;
-
- parse_header:
-
- // If download not at EOF, don't try to parse multi-part
- // boundary if any chance it would be split across buffers.
- if (hrRead != S_FALSE && m_pbDataBeg > m_pbDataEnd - BOUNDARY_MAXSIZE)
- {
- BufShift();
- continue;
- }
-
- // Attempt to parse the multi-part boundary.
- if (!ParseMultiPartHeader())
- break;
-
- // Check if we got termination boundary.
- if (m_dwRangeBeg == m_dwRangeEnd)
- break;
-
- goto process_data;
-
- process_data:
-
- // Calculate amount of data to report.
- DWORD cbData = m_pbDataEnd - m_pbDataBeg;
- DWORD cbRange = m_dwRangeEnd - m_dwRangeBeg;
- DWORD cbLength = min (cbData, cbRange);
-
- if (cbLength)
- {
- // Call back the client with more data.
- m_CBParam.dwDataOffset = m_dwRangeBeg;
- m_CBParam.lpDataBuffer = m_pbDataBeg;
- m_CBParam.cbDataLength = cbLength;
- if (!(*(m_pParam->pfnRequestCB)) (&m_CBParam))
- {
- // Client wants to stop.
- m_pBinding->Abort();
- break;
- }
- m_dwRangeBeg += cbLength;
- }
-
- // Adjust the buffer depending on next state.
- if (m_dwRangeBeg == m_dwRangeEnd) // (cbLength == cbRange)
- {
- // Consume the data and look for next header.
- m_pbDataBeg += cbLength;
- goto parse_header;
- }
- else
- {
- // Reset buffer and get some more data.
- BufReset();
- continue;
- }
-
- } while (hrRead == S_OK);
-
- return (hrRead == S_FALSE || hrRead == E_PENDING)? S_OK : hrRead;
- }
-
-