⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 http.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * Copyright 2005 Jacek Caban
 * Copyright 2007 Misha Koshelev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

/*
 * TODO:
 * - Handle redirects as native.
 */

#include <stdarg.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
#include "urlmon.h"
#include "wininet.h"
#include "urlmon_main.h"

#include "wine/debug.h"
#include "wine/unicode.h"

WINE_DEFAULT_DEBUG_CHANNEL(urlmon);

/* Flags are needed for, among other things, return HRESULTs from the Read function
 * to conform to native. For example, Read returns:
 *
 * 1. E_PENDING if called before the request has completed,
 *        (flags = 0)
 * 2. S_FALSE after all data has been read and S_OK has been reported,
 *        (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
 * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
 *    this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
 *        (flags = FLAG_REQUEST_COMPLETE)
 *    but upon subsequent calls to Read no reporting will take place, yet
 *    InternetQueryDataAvailable will still be called, and, on failure,
 *    INET_E_DATA_NOT_AVAILABLE will still be returned.
 *        (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
 *
 * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
 * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
 * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
 * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
 * if OnResponse does not return S_OK, Continue will not report data, and Read
 * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
 * data has been read.
 */
#define FLAG_REQUEST_COMPLETE 0x1
#define FLAG_FIRST_CONTINUE_COMPLETE 0x2
#define FLAG_FIRST_DATA_REPORTED 0x4
#define FLAG_ALL_DATA_READ 0x8
#define FLAG_LAST_DATA_REPORTED 0x10
#define FLAG_RESULT_REPORTED 0x20

typedef struct {
    const IInternetProtocolVtbl *lpInternetProtocolVtbl;
    const IInternetPriorityVtbl *lpInternetPriorityVtbl;

    DWORD flags, grfBINDF;
    BINDINFO bind_info;
    IInternetProtocolSink *protocol_sink;
    IHttpNegotiate *http_negotiate;
    HINTERNET internet, connect, request;
    LPWSTR full_header;
    HANDLE lock;
    ULONG current_position, content_length, available_bytes;
    LONG priority;

    LONG ref;
} HttpProtocol;

/* Default headers from native */
static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
                                   ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};

/*
 * Helpers
 */

static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
{
    if (!(This->flags & FLAG_RESULT_REPORTED) &&
        This->protocol_sink)
    {
        This->flags |= FLAG_RESULT_REPORTED;
        IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
    }
}

static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
{
    DWORD bscf;
    if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
        This->protocol_sink)
    {
        if (This->flags & FLAG_FIRST_DATA_REPORTED)
        {
            bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
        }
        else
        {
            This->flags |= FLAG_FIRST_DATA_REPORTED;
            bscf = BSCF_FIRSTDATANOTIFICATION;
        }
        if (This->flags & FLAG_ALL_DATA_READ &&
            !(This->flags & FLAG_LAST_DATA_REPORTED))
        {
            This->flags |= FLAG_LAST_DATA_REPORTED;
            bscf |= BSCF_LASTDATANOTIFICATION;
        }
        IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
                                         This->current_position+This->available_bytes,
                                         This->content_length);
    }
}

static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
{
    if (!(This->flags & FLAG_ALL_DATA_READ))
        This->flags |= FLAG_ALL_DATA_READ;
    HTTPPROTOCOL_ReportData(This);
    HTTPPROTOCOL_ReportResult(This, S_OK);
}

static void HTTPPROTOCOL_Close(HttpProtocol *This)
{
    if (This->protocol_sink)
    {
        IInternetProtocolSink_Release(This->protocol_sink);
        This->protocol_sink = 0;
    }
    if (This->http_negotiate)
    {
        IHttpNegotiate_Release(This->http_negotiate);
        This->http_negotiate = 0;
    }
    if (This->request)
    {
        InternetCloseHandle(This->request);
        This->request = 0;
    }
    if (This->connect)
    {
        InternetCloseHandle(This->connect);
        This->connect = 0;
    }
    if (This->internet)
    {
        InternetCloseHandle(This->internet);
        This->internet = 0;
    }
    if (This->full_header)
    {
        if (This->full_header != wszHeaders)
            HeapFree(GetProcessHeap(), 0, This->full_header);
        This->full_header = 0;
    }
    if (This->bind_info.cbSize)
    {
        ReleaseBindInfo(&This->bind_info);
        memset(&This->bind_info, 0, sizeof(This->bind_info));
    }
    This->flags = 0;
}

static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
    HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
    LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
    HttpProtocol *This = (HttpProtocol *)dwContext;
    PROTOCOLDATA data;
    ULONG ulStatusCode;

    switch (dwInternetStatus)
    {
    case INTERNET_STATUS_RESOLVING_NAME:
        ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
        break;
    case INTERNET_STATUS_CONNECTING_TO_SERVER:
        ulStatusCode = BINDSTATUS_CONNECTING;
        break;
    case INTERNET_STATUS_SENDING_REQUEST:
        ulStatusCode = BINDSTATUS_SENDINGREQUEST;
        break;
    case INTERNET_STATUS_REQUEST_COMPLETE:
        This->flags |= FLAG_REQUEST_COMPLETE;
        /* PROTOCOLDATA same as native */
        memset(&data, 0, sizeof(data));
        data.dwState = 0xf1000000;
        if (This->flags & FLAG_FIRST_CONTINUE_COMPLETE)
            data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
        else
            data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
        if (This->grfBINDF & BINDF_FROMURLMON)
            IInternetProtocolSink_Switch(This->protocol_sink, &data);
        else
            IInternetProtocol_Continue((IInternetProtocol *)This, &data);
        return;
    default:
        WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
        return;
    }

    IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
}

static inline LPWSTR strndupW(LPWSTR string, int len)
{
    LPWSTR ret = NULL;
    if (string &&
        (ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))) != NULL)
    {
        memcpy(ret, string, len*sizeof(WCHAR));
        ret[len] = 0;
    }
    return ret;
}

/*
 * Interface implementations
 */

#define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
#define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)

#define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)

static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);

    *ppv = NULL;
    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
    }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
    }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
        TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
    }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
        TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
        *ppv = PRIORITY(This);
    }

    if(*ppv) {
        IInternetProtocol_AddRef(iface);
        return S_OK;
    }

    WARN("not supported interface %s\n", debugstr_guid(riid));
    return E_NOINTERFACE;
}

static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    LONG ref = InterlockedIncrement(&This->ref);
    TRACE("(%p) ref=%d\n", This, ref);
    return ref;
}

static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    if(!ref) {
        HTTPPROTOCOL_Close(This);
        HeapFree(GetProcessHeap(), 0, This);

        URLMON_UnlockModule();
    }

    return ref;
}

static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
        DWORD grfPI, DWORD dwReserved)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    URL_COMPONENTSW url;
    DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
    ULONG num = 0;
    IServiceProvider *service_provider = 0;
    IHttpNegotiate2 *http_negotiate2 = 0;
    LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
        post_cookie = 0, optional = 0;
    BYTE security_id[512];
    LPOLESTR user_agent, accept_mimes[257];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -