protocol.c 16.8 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 34 35 36 37

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(itss);

typedef struct {
38
    IUnknown              IUnknown_inner;
39 40
    IInternetProtocol     IInternetProtocol_iface;
    IInternetProtocolInfo IInternetProtocolInfo_iface;
41

42
    LONG ref;
43
    IUnknown *outer;
44

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

50 51 52 53 54
static inline ITSProtocol *impl_from_IUnknown(IUnknown *iface)
{
    return CONTAINING_RECORD(iface, ITSProtocol, IUnknown_inner);
}

55 56 57 58 59 60 61 62 63
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);
}
64

65 66 67 68 69 70 71 72 73
static void release_chm(ITSProtocol *This)
{
    if(This->chm_file) {
        chm_close(This->chm_file);
        This->chm_file = NULL;
    }
    This->offset = 0;
}

74
static HRESULT WINAPI ITSProtocol_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
75
{
76
    ITSProtocol *This = impl_from_IUnknown(iface);
77 78 79

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
80
        *ppv = &This->IUnknown_inner;
81 82
    }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
83
        *ppv = &This->IInternetProtocol_iface;
84 85
    }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
        TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
86
        *ppv = &This->IInternetProtocol_iface;
87 88
    }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv);
89
        *ppv = &This->IInternetProtocolInfo_iface;
90 91 92 93
    }else {
        *ppv = NULL;
        WARN("not supported interface %s\n", debugstr_guid(riid));
        return E_NOINTERFACE;
94 95
    }

96 97
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
98 99
}

100
static ULONG WINAPI ITSProtocol_AddRef(IUnknown *iface)
101
{
102
    ITSProtocol *This = impl_from_IUnknown(iface);
103
    LONG ref = InterlockedIncrement(&This->ref);
104
    TRACE("(%p) ref=%ld\n", This, ref);
105 106 107
    return ref;
}

108
static ULONG WINAPI ITSProtocol_Release(IUnknown *iface)
109
{
110
    ITSProtocol *This = impl_from_IUnknown(iface);
111 112
    LONG ref = InterlockedDecrement(&This->ref);

113
    TRACE("(%p) ref=%ld\n", This, ref);
114 115

    if(!ref) {
116
        release_chm(This);
117
        HeapFree(GetProcessHeap(), 0, This);
118

119 120 121 122 123 124
        ITSS_UnlockModule();
    }

    return ref;
}

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
static const IUnknownVtbl ITSProtocolUnkVtbl = {
    ITSProtocol_QueryInterface,
    ITSProtocol_AddRef,
    ITSProtocol_Release
};

static HRESULT WINAPI ITSInternetProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
{
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
    return IUnknown_QueryInterface(This->outer, riid, ppv);
}

static ULONG WINAPI ITSInternetProtocol_AddRef(IInternetProtocol *iface)
{
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
    return IUnknown_AddRef(This->outer);
}

static ULONG WINAPI ITSInternetProtocol_Release(IInternetProtocol *iface)
{
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
    return IUnknown_Release(This->outer);
}

149 150
static LPCWSTR skip_schema(LPCWSTR url)
{
151 152 153 154 155 156 157 158 159 160
    static const WCHAR its_schema[] = L"its:";
    static const WCHAR msits_schema[] = L"ms-its:";
    static const WCHAR mk_schema[] = L"mk:@MSITStore:";

    if(!wcsnicmp(its_schema, url, ARRAY_SIZE(its_schema) - 1))
        return url + ARRAY_SIZE(its_schema) - 1;
    if(!wcsnicmp(msits_schema, url, ARRAY_SIZE(msits_schema) - 1))
        return url + ARRAY_SIZE(msits_schema) - 1;
    if(!wcsnicmp(mk_schema, url, ARRAY_SIZE(mk_schema) - 1))
        return url + ARRAY_SIZE(mk_schema) - 1;
161 162 163 164

    return NULL;
}

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
/* 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;
        }
    }
}

214 215 216 217 218 219
static HRESULT report_result(IInternetProtocolSink *sink, HRESULT hres)
{
    IInternetProtocolSink_ReportResult(sink, hres, 0, NULL);
    return hres;
}

220 221
static HRESULT WINAPI ITSProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
222
        DWORD grfPI, HANDLE_PTR dwReserved)
223
{
224
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
225 226
    BINDINFO bindinfo;
    DWORD bindf = 0, len;
227 228
    LPWSTR file_name, mime, object_name, p;
    LPCWSTR ptr;
229 230 231 232 233
    struct chmFile *chm_file;
    struct chmUnitInfo chm_object;
    int res;
    HRESULT hres;

234
    TRACE("(%p)->(%s %p %p %08lx %Ix)\n", This, debugstr_w(szUrl), pOIProtSink,
235
            pOIBindInfo, grfPI, dwReserved);
236

237 238
    ptr = skip_schema(szUrl);
    if(!ptr)
239 240 241 242 243 244
        return INET_E_USE_DEFAULT_PROTOCOLHANDLER;

    memset(&bindinfo, 0, sizeof(bindinfo));
    bindinfo.cbSize = sizeof(BINDINFO);
    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo);
    if(FAILED(hres)) {
245
        WARN("GetBindInfo failed: %08lx\n", hres);
246 247 248 249 250
        return hres;
    }

    ReleaseBindInfo(&bindinfo);

251
    len = lstrlenW(ptr)+3;
252 253 254 255
    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)) {
256
        WARN("UrlUnescape failed: %08lx\n", hres);
257 258 259 260
        HeapFree(GetProcessHeap(), 0, file_name);
        return hres;
    }

261
    p = wcsstr(file_name, L"::");
262
    if(!p) {
263
        WARN("invalid url\n");
264
        HeapFree(GetProcessHeap(), 0, file_name);
265 266 267
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

268
    *p = 0;
269 270 271
    chm_file = chm_openW(file_name);
    if(!chm_file) {
        WARN("Could not open chm file\n");
272
        HeapFree(GetProcessHeap(), 0, file_name);
273 274 275
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

276
    object_name = p+2;
277
    len = lstrlenW(object_name);
278

279
    if(*object_name != '/' && *object_name != '\\') {
280
        memmove(object_name+1, object_name, (len+1)*sizeof(WCHAR));
281
        *object_name = '/';
282
        len++;
283 284
    }

285 286 287
    if(object_name[len-1] == '/')
        object_name[--len] = 0;

288 289 290 291 292
    for(p=object_name; *p; p++) {
        if(*p == '\\')
            *p = '/';
    }

293 294
    remove_dot_segments(object_name);

295 296
    TRACE("Resolving %s\n", debugstr_w(object_name));

297 298 299 300
    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
301
        HeapFree(GetProcessHeap(), 0, file_name);
302 303 304 305
        chm_close(chm_file);
        return report_result(pOIProtSink, STG_E_FILENOTFOUND);
    }

306
    IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST,
307
                                         wcsrchr(object_name, '/')+1);
308 309

    /* FIXME: Native doesn't use FindMimeFromData */
310
    hres = FindMimeFromData(NULL, object_name, NULL, 0, NULL, 0, &mime, 0);
311
    HeapFree(GetProcessHeap(), 0, file_name);
312 313 314 315 316
    if(SUCCEEDED(hres)) {
        IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime);
        CoTaskMemFree(mime);
    }

317 318
    release_chm(This); /* Native leaks handle here */
    This->chm_file = chm_file;
319
    This->chm_object = chm_object;
320

321 322 323 324
    hres = IInternetProtocolSink_ReportData(pOIProtSink,
            BSCF_FIRSTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE,
            chm_object.length, chm_object.length);
    if(FAILED(hres)) {
325
        WARN("ReportData failed: %08lx\n", hres);
326
        release_chm(This);
327 328 329 330 331 332
        return report_result(pOIProtSink, hres);
    }

    hres = IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_BEGINDOWNLOADDATA, NULL);

    return report_result(pOIProtSink, hres);
333 334 335 336
}

static HRESULT WINAPI ITSProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
{
337
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
338 339 340 341 342 343 344
    FIXME("(%p)->(%p)\n", This, pProtocolData);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
        DWORD dwOptions)
{
345
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
346
    FIXME("(%p)->(%08lx %08lx)\n", This, hrReason, dwOptions);
347 348 349 350 351
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
{
352
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
353

354
    TRACE("(%p)->(%08lx)\n", This, dwOptions);
355 356

    return S_OK;
357 358 359 360
}

static HRESULT WINAPI ITSProtocol_Suspend(IInternetProtocol *iface)
{
361
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
362 363 364 365 366 367
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Resume(IInternetProtocol *iface)
{
368
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
369 370 371 372 373 374 375
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_Read(IInternetProtocol *iface, void *pv,
        ULONG cb, ULONG *pcbRead)
{
376
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
377

378
    TRACE("(%p)->(%p %lu %p)\n", This, pv, cb, pcbRead);
379 380 381 382 383 384 385 386

    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;
387 388 389
}

static HRESULT WINAPI ITSProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
390
        DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
391
{
392
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
393
    FIXME("(%p)->(%ld %ld %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
394 395 396 397 398
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
{
399
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
400

401
    TRACE("(%p)->(%08lx)\n", This, dwOptions);
402 403

    return S_OK;
404 405 406 407
}

static HRESULT WINAPI ITSProtocol_UnlockRequest(IInternetProtocol *iface)
{
408
    ITSProtocol *This = impl_from_IInternetProtocol(iface);
409 410 411 412

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

    return S_OK;
413 414 415
}

static const IInternetProtocolVtbl ITSProtocolVtbl = {
416 417 418
    ITSInternetProtocol_QueryInterface,
    ITSInternetProtocol_AddRef,
    ITSInternetProtocol_Release,
419 420 421 422 423 424 425 426 427 428 429 430
    ITSProtocol_Start,
    ITSProtocol_Continue,
    ITSProtocol_Abort,
    ITSProtocol_Terminate,
    ITSProtocol_Suspend,
    ITSProtocol_Resume,
    ITSProtocol_Read,
    ITSProtocol_Seek,
    ITSProtocol_LockRequest,
    ITSProtocol_UnlockRequest
};

431 432 433
static HRESULT WINAPI ITSProtocolInfo_QueryInterface(IInternetProtocolInfo *iface,
                                              REFIID riid, void **ppv)
{
434 435
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_QueryInterface(&This->IInternetProtocol_iface, riid, ppv);
436 437 438 439
}

static ULONG WINAPI ITSProtocolInfo_AddRef(IInternetProtocolInfo *iface)
{
440 441
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_AddRef(&This->IInternetProtocol_iface);
442 443 444 445
}

static ULONG WINAPI ITSProtocolInfo_Release(IInternetProtocolInfo *iface)
{
446 447
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
    return IInternetProtocol_Release(&This->IInternetProtocol_iface);
448 449 450 451 452 453
}

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

456
    TRACE("(%p)->(%s %x %08lx %p %ld %p %ld)\n", This, debugstr_w(pwzUrl), ParseAction,
457
          dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
458 459 460 461 462 463 464 465 466 467 468 469 470

    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;
471 472 473 474 475 476
}

static HRESULT WINAPI ITSProtocolInfo_CombineUrl(IInternetProtocolInfo *iface,
        LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult,
        DWORD cchResult, DWORD* pcchResult, DWORD dwReserved)
{
477
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
478 479 480
    LPCWSTR base_end, ptr;
    DWORD rel_len;

481
    TRACE("(%p)->(%s %s %08lx %p %ld %p %ld)\n", This, debugstr_w(pwzBaseUrl),
482 483
            debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult,
            pcchResult, dwReserved);
484

485
    base_end = wcsstr(pwzBaseUrl, L"::");
486 487 488 489 490 491 492
    if(!base_end)
        return 0x80041001;
    base_end += 2;

    if(!skip_schema(pwzBaseUrl))
        return INET_E_USE_DEFAULT_PROTOCOLHANDLER;

493
    if(wcschr(pwzRelativeUrl, ':'))
494 495
        return STG_E_INVALIDNAME;

496
    if(pwzRelativeUrl[0] == '#') {
497
        base_end += lstrlenW(base_end);
498
    }else if(pwzRelativeUrl[0] != '/') {
499
        ptr = wcsrchr(base_end, '/');
500 501 502
        if(ptr)
            base_end = ptr+1;
        else
503
            base_end += lstrlenW(base_end);
504 505
    }

506
    rel_len = lstrlenW(pwzRelativeUrl)+1;
507 508 509 510 511 512 513

    *pcchResult = rel_len + (base_end-pwzBaseUrl);

    if(*pcchResult > cchResult)
        return E_OUTOFMEMORY;

    memcpy(pwzResult, pwzBaseUrl, (base_end-pwzBaseUrl)*sizeof(WCHAR));
514
    lstrcpyW(pwzResult + (base_end-pwzBaseUrl), pwzRelativeUrl);
515 516

    return S_OK;
517 518 519 520 521
}

static HRESULT WINAPI ITSProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1,
        LPCWSTR pwzUrl2, DWORD dwCompareFlags)
{
522
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
523
    FIXME("%p)->(%s %s %08lx)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags);
524 525 526 527 528 529 530
    return E_NOTIMPL;
}

static HRESULT WINAPI ITSProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
        QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
        DWORD dwReserved)
{
531
    ITSProtocol *This = impl_from_IInternetProtocolInfo(iface);
532
    FIXME("(%p)->(%s %08x %08lx %p %ld %p %ld)\n", This, debugstr_w(pwzUrl), QueryOption,
533 534 535 536 537 538 539 540 541 542 543 544 545 546
          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
};

547
HRESULT ITSProtocol_create(IUnknown *outer, void **ppv)
548 549 550
{
    ITSProtocol *ret;

551
    TRACE("(%p %p)\n", outer, ppv);
552 553 554

    ITSS_LockModule();

555
    ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ITSProtocol));
556 557
    if(!ret)
        return E_OUTOFMEMORY;
558

559
    ret->IUnknown_inner.lpVtbl = &ITSProtocolUnkVtbl;
560 561
    ret->IInternetProtocol_iface.lpVtbl = &ITSProtocolVtbl;
    ret->IInternetProtocolInfo_iface.lpVtbl = &ITSProtocolInfoVtbl;
562
    ret->ref = 1;
563
    ret->outer = outer ? outer : &ret->IUnknown_inner;
564

565
    *ppv = &ret->IUnknown_inner;
566 567
    return S_OK;
}