protocol.c 16 KB
Newer Older
1
/*
2
 * Copyright 2006-2007 Jacek Caban for CodeWeavers
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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
 */

#include <stdarg.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
26
#include "winreg.h"
27 28
#include "ole2.h"
#include "urlmon.h"
29
#include "shlwapi.h"
30
#include "itsstor.h"
31
#include "chm_lib.h"
32 33

#include "wine/debug.h"
34
#include "wine/unicode.h"
35 36 37 38

WINE_DEFAULT_DEBUG_CHANNEL(itss);

typedef struct {
39 40
    IInternetProtocol     IInternetProtocol_iface;
    IInternetProtocolInfo IInternetProtocolInfo_iface;
41

42 43
    LONG ref;

44 45 46 47
    ULONG offset;
    struct chmFile *chm_file;
    struct chmUnitInfo chm_object;
} ITSProtocol;
48

49 50 51 52 53 54 55 56 57
static inline ITSProtocol *impl_from_IInternetProtocol(IInternetProtocol *iface)
{
    return CONTAINING_RECORD(iface, ITSProtocol, IInternetProtocol_iface);
}

static inline ITSProtocol *impl_from_IInternetProtocolInfo(IInternetProtocolInfo *iface)
{
    return CONTAINING_RECORD(iface, ITSProtocol, IInternetProtocolInfo_iface);
}
58

59 60 61 62 63 64 65 66 67
static void release_chm(ITSProtocol *This)
{
    if(This->chm_file) {
        chm_close(This->chm_file);
        This->chm_file = NULL;
    }
    This->offset = 0;
}

68 69
static HRESULT WINAPI ITSProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
{
70
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
71 72 73 74

    *ppv = NULL;
    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
75
        *ppv = &This->IInternetProtocol_iface;
76 77
    }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
78
        *ppv = &This->IInternetProtocol_iface;
79 80
    }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
        TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
81
        *ppv = &This->IInternetProtocol_iface;
82 83
    }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv);
84
        *ppv = &This->IInternetProtocolInfo_iface;
85 86 87 88 89 90 91 92 93 94 95 96 97
    }

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

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

static ULONG WINAPI ITSProtocol_AddRef(IInternetProtocol *iface)
{
98
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
99 100 101 102 103 104 105
    LONG ref = InterlockedIncrement(&This->ref);
    TRACE("(%p) ref=%d\n", This, ref);
    return ref;
}

static ULONG WINAPI ITSProtocol_Release(IInternetProtocol *iface)
{
106
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
107 108 109 110 111
    LONG ref = InterlockedDecrement(&This->ref);

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

    if(!ref) {
112
        release_chm(This);
113
        HeapFree(GetProcessHeap(), 0, This);
114

115 116 117 118 119 120
        ITSS_UnlockModule();
    }

    return ref;
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static LPCWSTR skip_schema(LPCWSTR url)
{
    static const WCHAR its_schema[] = {'i','t','s',':'};
    static const WCHAR msits_schema[] = {'m','s','-','i','t','s',':'};
    static const WCHAR mk_schema[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':'};

    if(!strncmpiW(its_schema, url, sizeof(its_schema)/sizeof(WCHAR)))
        return url+sizeof(its_schema)/sizeof(WCHAR);
    if(!strncmpiW(msits_schema, url, sizeof(msits_schema)/sizeof(WCHAR)))
        return url+sizeof(msits_schema)/sizeof(WCHAR);
    if(!strncmpiW(mk_schema, url, sizeof(mk_schema)/sizeof(WCHAR)))
        return url+sizeof(mk_schema)/sizeof(WCHAR);

    return NULL;
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
/* Adopted from urlmon */
static void remove_dot_segments(WCHAR *path) {
    const WCHAR *in = path;
    WCHAR *out = path;

    while(1) {
        /* Move the first path segment in the input buffer to the end of
         * the output buffer, and any subsequent characters up to, including
         * the next "/" character (if any) or the end of the input buffer.
         */
        while(*in != '/') {
            if(!(*out++ = *in++))
                return;
        }

        *out++ = *in++;

        while(*in) {
            if(*in != '.')
                break;

            /* Handle ending "/." */
            if(!in[1]) {
                ++in;
                break;
            }

            /* Handle "/./" */
            if(in[1] == '/') {
                in += 2;
                continue;
            }

            /* If we don't have "/../" or ending "/.." */
            if(in[1] != '.' || (in[2] && in[2] != '/'))
                break;

            in += *in ? 3 : 2;

            /* Find the slash preceding out pointer and move out pointer to it */
            if(out > path+1 && *--out == '/')
                --out;
            while(out > path && *(--out) != '/');
            if(*out == '/')
                ++out;
        }
    }
}

186 187 188 189 190 191
static HRESULT report_result(IInternetProtocolSink *sink, HRESULT hres)
{
    IInternetProtocolSink_ReportResult(sink, hres, 0, NULL);
    return hres;
}

192 193
static HRESULT WINAPI ITSProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
194
        DWORD grfPI, HANDLE_PTR dwReserved)
195
{
196
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
197 198
    BINDINFO bindinfo;
    DWORD bindf = 0, len;
199 200
    LPWSTR file_name, mime, object_name, p;
    LPCWSTR ptr;
201 202 203 204 205 206 207
    struct chmFile *chm_file;
    struct chmUnitInfo chm_object;
    int res;
    HRESULT hres;

    static const WCHAR separator[] = {':',':',0};

208
    TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
209
            pOIBindInfo, grfPI, dwReserved);
210

211 212
    ptr = skip_schema(szUrl);
    if(!ptr)
213 214 215 216 217 218 219 220 221 222 223 224
        return INET_E_USE_DEFAULT_PROTOCOLHANDLER;

    memset(&bindinfo, 0, sizeof(bindinfo));
    bindinfo.cbSize = sizeof(BINDINFO);
    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo);
    if(FAILED(hres)) {
        WARN("GetBindInfo failed: %08x\n", hres);
        return hres;
    }

    ReleaseBindInfo(&bindinfo);

225 226 227 228 229 230 231 232 233 234 235 236
    len = strlenW(ptr)+3;
    file_name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
    memcpy(file_name, ptr, len*sizeof(WCHAR));
    hres = UrlUnescapeW(file_name, NULL, &len, URL_UNESCAPE_INPLACE);
    if(FAILED(hres)) {
        WARN("UrlUnescape failed: %08x\n", hres);
        HeapFree(GetProcessHeap(), 0, file_name);
        return hres;
    }

    p = strstrW(file_name, separator);
    if(!p) {
237
        WARN("invalid url\n");
238
        HeapFree(GetProcessHeap(), 0, file_name);
239 240 241
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

242
    *p = 0;
243 244 245
    chm_file = chm_openW(file_name);
    if(!chm_file) {
        WARN("Could not open chm file\n");
246
        HeapFree(GetProcessHeap(), 0, file_name);
247 248 249
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

250
    object_name = p+2;
251 252
    len = strlenW(object_name);

253
    if(*object_name != '/' && *object_name != '\\') {
254
        memmove(object_name+1, object_name, (len+1)*sizeof(WCHAR));
255
        *object_name = '/';
256
        len++;
257 258
    }

259 260 261
    if(object_name[len-1] == '/')
        object_name[--len] = 0;

262 263 264 265 266
    for(p=object_name; *p; p++) {
        if(*p == '\\')
            *p = '/';
    }

267 268
    remove_dot_segments(object_name);

269 270
    TRACE("Resolving %s\n", debugstr_w(object_name));

271 272 273 274
    memset(&chm_object, 0, sizeof(chm_object));
    res = chm_resolve_object(chm_file, object_name, &chm_object);
    if(res != CHM_RESOLVE_SUCCESS) {
        WARN("Could not resolve chm object\n");
Andrew Talbot's avatar
Andrew Talbot committed
275
        HeapFree(GetProcessHeap(), 0, file_name);
276 277 278 279
        chm_close(chm_file);
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

280
    IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST,
281
                                         strrchrW(object_name, '/')+1);
282 283

    /* FIXME: Native doesn't use FindMimeFromData */
284
    hres = FindMimeFromData(NULL, object_name, NULL, 0, NULL, 0, &mime, 0);
285
    HeapFree(GetProcessHeap(), 0, file_name);
286 287 288 289 290
    if(SUCCEEDED(hres)) {
        IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime);
        CoTaskMemFree(mime);
    }

291 292
    release_chm(This); /* Native leaks handle here */
    This->chm_file = chm_file;
293
    This->chm_object = chm_object;
294

295 296 297 298 299
    hres = IInternetProtocolSink_ReportData(pOIProtSink,
            BSCF_FIRSTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE,
            chm_object.length, chm_object.length);
    if(FAILED(hres)) {
        WARN("ReportData failed: %08x\n", hres);
300
        release_chm(This);
301 302 303 304 305 306
        return report_result(pOIProtSink, hres);
    }

    hres = IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_BEGINDOWNLOADDATA, NULL);

    return report_result(pOIProtSink, hres);
307 308 309 310
}

static HRESULT WINAPI ITSProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
{
311
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
312 313 314 315 316 317 318
    FIXME("(%p)->(%p)\n", This, pProtocolData);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
        DWORD dwOptions)
{
319
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
320 321 322 323 324 325
    FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
{
326
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
327 328 329 330

    TRACE("(%p)->(%08x)\n", This, dwOptions);

    return S_OK;
331 332 333 334
}

static HRESULT WINAPI ITSProtocol_Suspend(IInternetProtocol *iface)
{
335
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
336 337 338 339 340 341
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Resume(IInternetProtocol *iface)
{
342
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
343 344 345 346 347 348 349
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Read(IInternetProtocol *iface, void *pv,
        ULONG cb, ULONG *pcbRead)
{
350
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
351 352 353 354 355 356 357 358 359 360

    TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);

    if(!This->chm_file)
        return INET_E_DATA_NOT_AVAILABLE;

    *pcbRead = chm_retrieve_object(This->chm_file, &This->chm_object, pv, This->offset, cb);
    This->offset += *pcbRead;

    return *pcbRead ? S_OK : S_FALSE;
361 362 363
}

static HRESULT WINAPI ITSProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
364
        DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
365
{
366
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
367
    FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
368 369 370 371 372
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
{
373
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
374 375 376 377

    TRACE("(%p)->(%08x)\n", This, dwOptions);

    return S_OK;
378 379 380 381
}

static HRESULT WINAPI ITSProtocol_UnlockRequest(IInternetProtocol *iface)
{
382
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
383 384 385 386

    TRACE("(%p)\n", This);

    return S_OK;
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
}

static const IInternetProtocolVtbl ITSProtocolVtbl = {
    ITSProtocol_QueryInterface,
    ITSProtocol_AddRef,
    ITSProtocol_Release,
    ITSProtocol_Start,
    ITSProtocol_Continue,
    ITSProtocol_Abort,
    ITSProtocol_Terminate,
    ITSProtocol_Suspend,
    ITSProtocol_Resume,
    ITSProtocol_Read,
    ITSProtocol_Seek,
    ITSProtocol_LockRequest,
    ITSProtocol_UnlockRequest
};

405 406 407
static HRESULT WINAPI ITSProtocolInfo_QueryInterface(IInternetProtocolInfo *iface,
                                              REFIID riid, void **ppv)
{
408 409
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_QueryInterface(&This->IInternetProtocol_iface, riid, ppv);
410 411 412 413
}

static ULONG WINAPI ITSProtocolInfo_AddRef(IInternetProtocolInfo *iface)
{
414 415
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_AddRef(&This->IInternetProtocol_iface);
416 417 418 419
}

static ULONG WINAPI ITSProtocolInfo_Release(IInternetProtocolInfo *iface)
{
420 421
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_Release(&This->IInternetProtocol_iface);
422 423 424 425 426 427
}

static HRESULT WINAPI ITSProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
        PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
        DWORD *pcchResult, DWORD dwReserved)
{
428
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
429 430

    TRACE("(%p)->(%s %x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), ParseAction,
431
          dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
432 433 434 435 436 437 438 439 440 441 442 443 444

    switch(ParseAction) {
    case PARSE_CANONICALIZE:
        FIXME("PARSE_CANONICALIZE\n");
        return E_NOTIMPL;
    case PARSE_SECURITY_URL:
        FIXME("PARSE_SECURITY_URL\n");
        return E_NOTIMPL;
    default:
        return INET_E_DEFAULT_ACTION;
    }

    return S_OK;
445 446 447 448 449 450
}

static HRESULT WINAPI ITSProtocolInfo_CombineUrl(IInternetProtocolInfo *iface,
        LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult,
        DWORD cchResult, DWORD* pcchResult, DWORD dwReserved)
{
451
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
452 453 454 455 456 457
    LPCWSTR base_end, ptr;
    DWORD rel_len;

    static const WCHAR separator[] = {':',':',0};

    TRACE("(%p)->(%s %s %08x %p %d %p %d)\n", This, debugstr_w(pwzBaseUrl),
458 459
            debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult,
            pcchResult, dwReserved);
460 461 462 463 464 465 466 467 468 469 470 471

    base_end = strstrW(pwzBaseUrl, separator);
    if(!base_end)
        return 0x80041001;
    base_end += 2;

    if(!skip_schema(pwzBaseUrl))
        return INET_E_USE_DEFAULT_PROTOCOLHANDLER;

    if(strchrW(pwzRelativeUrl, ':'))
        return STG_E_INVALIDNAME;

472 473 474
    if(pwzRelativeUrl[0] == '#') {
        base_end += strlenW(base_end);
    }else if(pwzRelativeUrl[0] != '/') {
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
        ptr = strrchrW(base_end, '/');
        if(ptr)
            base_end = ptr+1;
        else
            base_end += strlenW(base_end);
    }

    rel_len = strlenW(pwzRelativeUrl)+1;

    *pcchResult = rel_len + (base_end-pwzBaseUrl);

    if(*pcchResult > cchResult)
        return E_OUTOFMEMORY;

    memcpy(pwzResult, pwzBaseUrl, (base_end-pwzBaseUrl)*sizeof(WCHAR));
    strcpyW(pwzResult + (base_end-pwzBaseUrl), pwzRelativeUrl);

    return S_OK;
493 494 495 496 497
}

static HRESULT WINAPI ITSProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1,
        LPCWSTR pwzUrl2, DWORD dwCompareFlags)
{
498
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
499 500 501 502 503 504 505 506
    FIXME("%p)->(%s %s %08x)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
        QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
        DWORD dwReserved)
{
507
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    FIXME("(%p)->(%s %08x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), QueryOption,
          dwQueryFlags, pBuffer, cbBuffer, pcbBuf, dwReserved);
    return E_NOTIMPL;
}

static const IInternetProtocolInfoVtbl ITSProtocolInfoVtbl = {
    ITSProtocolInfo_QueryInterface,
    ITSProtocolInfo_AddRef,
    ITSProtocolInfo_Release,
    ITSProtocolInfo_ParseUrl,
    ITSProtocolInfo_CombineUrl,
    ITSProtocolInfo_CompareUrl,
    ITSProtocolInfo_QueryInfo
};

523 524 525 526 527 528 529 530
HRESULT ITSProtocol_create(IUnknown *pUnkOuter, LPVOID *ppobj)
{
    ITSProtocol *ret;

    TRACE("(%p %p)\n", pUnkOuter, ppobj);

    ITSS_LockModule();

531
    ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ITSProtocol));
532

533 534
    ret->IInternetProtocol_iface.lpVtbl = &ITSProtocolVtbl;
    ret->IInternetProtocolInfo_iface.lpVtbl = &ITSProtocolInfoVtbl;
535 536
    ret->ref = 1;

537
    *ppobj = &ret->IInternetProtocol_iface;
538 539 540

    return S_OK;
}