session.c 22 KB
Newer Older
1
/*
2
 * Copyright 2005-2006 Jacek Caban for CodeWeavers
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 18 19
 */

#include "urlmon_main.h"
Jacek Caban's avatar
Jacek Caban committed
20
#include "winreg.h"
21 22 23 24 25

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(urlmon);

26
typedef struct {
27 28
    LPWSTR protocol;
    IClassFactory *cf;
29
    CLSID clsid;
30
    BOOL urlmon;
31

32
    struct list entry;
33 34
} name_space;

35
typedef struct {
36 37 38 39
    IClassFactory *cf;
    CLSID clsid;
    LPWSTR mime;

40
    struct list entry;
41 42
} mime_filter;

43 44
static struct list name_space_list = LIST_INIT(name_space_list);
static struct list mime_filter_list = LIST_INIT(mime_filter_list);
45

46 47 48 49 50 51 52 53 54
static CRITICAL_SECTION session_cs;
static CRITICAL_SECTION_DEBUG session_cs_dbg =
{
    0, 0, &session_cs,
    { &session_cs_dbg.ProcessLocksList, &session_cs_dbg.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": session") }
};
static CRITICAL_SECTION session_cs = { &session_cs_dbg, -1, 0, 0, 0, 0 };

55 56 57 58 59 60 61
static const WCHAR internet_settings_keyW[] =
    {'S','O','F','T','W','A','R','E',
     '\\','M','i','c','r','o','s','o','f','t',
     '\\','W','i','n','d','o','w','s',
     '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
     '\\','I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};

62
static name_space *find_name_space(LPCWSTR protocol)
63
{
64 65
    name_space *iter;

66
    LIST_FOR_EACH_ENTRY(iter, &name_space_list, name_space, entry) {
67
        if(!strcmpiW(iter->protocol, protocol))
68
            return iter;
69 70 71 72 73
    }

    return NULL;
}

74
static HRESULT get_protocol_cf(LPCWSTR schema, DWORD schema_len, CLSID *pclsid, IClassFactory **ret)
75 76
{
    WCHAR str_clsid[64];
77
    HKEY hkey = NULL;
78
    DWORD res, type, size;
79 80 81 82 83 84 85 86
    CLSID clsid;
    LPWSTR wszKey;
    HRESULT hres;

    static const WCHAR wszProtocolsKey[] =
        {'P','R','O','T','O','C','O','L','S','\\','H','a','n','d','l','e','r','\\'};
    static const WCHAR wszCLSID[] = {'C','L','S','I','D',0};

87
    wszKey = heap_alloc(sizeof(wszProtocolsKey)+(schema_len+1)*sizeof(WCHAR));
88 89 90 91
    memcpy(wszKey, wszProtocolsKey, sizeof(wszProtocolsKey));
    memcpy(wszKey + sizeof(wszProtocolsKey)/sizeof(WCHAR), schema, (schema_len+1)*sizeof(WCHAR));

    res = RegOpenKeyW(HKEY_CLASSES_ROOT, wszKey, &hkey);
92
    heap_free(wszKey);
93
    if(res != ERROR_SUCCESS) {
94
        TRACE("Could not open protocol handler key\n");
95
        return MK_E_SYNTAX;
96 97 98 99 100 101
    }
    
    size = sizeof(str_clsid);
    res = RegQueryValueExW(hkey, wszCLSID, NULL, &type, (LPBYTE)str_clsid, &size);
    RegCloseKey(hkey);
    if(res != ERROR_SUCCESS || type != REG_SZ) {
102
        WARN("Could not get protocol CLSID res=%d\n", res);
103
        return MK_E_SYNTAX;
104 105 106 107
    }

    hres = CLSIDFromString(str_clsid, &clsid);
    if(FAILED(hres)) {
108
        WARN("CLSIDFromString failed: %08x\n", hres);
109 110 111
        return hres;
    }

112 113 114
    if(pclsid)
        *pclsid = clsid;

115 116 117
    if(!ret)
        return S_OK;

118 119
    hres = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)ret);
    return SUCCEEDED(hres) ? S_OK : MK_E_SYNTAX;
120 121
}

122
HRESULT register_namespace(IClassFactory *cf, REFIID clsid, LPCWSTR protocol, BOOL urlmon_protocol)
123 124 125 126 127 128 129 130 131
{
    name_space *new_name_space;

    new_name_space = heap_alloc(sizeof(name_space));

    if(!urlmon_protocol)
        IClassFactory_AddRef(cf);
    new_name_space->cf = cf;
    new_name_space->clsid = *clsid;
132
    new_name_space->urlmon = urlmon_protocol;
133 134
    new_name_space->protocol = heap_strdupW(protocol);

135 136
    EnterCriticalSection(&session_cs);

137
    list_add_head(&name_space_list, &new_name_space->entry);
138

139 140
    LeaveCriticalSection(&session_cs);

141 142 143
    return S_OK;
}

144
static HRESULT unregister_namespace(IClassFactory *cf, LPCWSTR protocol)
145
{
146
    name_space *iter;
147

148 149
    EnterCriticalSection(&session_cs);

150
    LIST_FOR_EACH_ENTRY(iter, &name_space_list, name_space, entry) {
151
        if(iter->cf == cf && !strcmpiW(iter->protocol, protocol)) {
152
            list_remove(&iter->entry);
153

154
            LeaveCriticalSection(&session_cs);
155

156 157 158 159 160 161
            if(!iter->urlmon)
                IClassFactory_Release(iter->cf);
            heap_free(iter->protocol);
            heap_free(iter);
            return S_OK;
        }
162 163
    }

164
    LeaveCriticalSection(&session_cs);
165 166 167
    return S_OK;
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181
BOOL is_registered_protocol(LPCWSTR url)
{
    DWORD schema_len;
    WCHAR schema[64];
    HRESULT hres;

    hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
            &schema_len, 0);
    if(FAILED(hres))
        return FALSE;

    return get_protocol_cf(schema, schema_len, NULL, NULL) == S_OK;
}

182 183 184
IInternetProtocolInfo *get_protocol_info(LPCWSTR url)
{
    IInternetProtocolInfo *ret = NULL;
185
    IClassFactory *cf;
186
    name_space *ns;
187 188 189 190 191 192 193 194 195
    WCHAR schema[64];
    DWORD schema_len;
    HRESULT hres;

    hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
            &schema_len, 0);
    if(FAILED(hres) || !schema_len)
        return NULL;

196
    EnterCriticalSection(&session_cs);
197

198 199
    ns = find_name_space(schema);
    if(ns && !ns->urlmon) {
200
        hres = IClassFactory_QueryInterface(ns->cf, &IID_IInternetProtocolInfo, (void**)&ret);
201 202
        if(FAILED(hres))
            hres = IClassFactory_CreateInstance(ns->cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
203 204
    }

205 206 207 208 209
    LeaveCriticalSection(&session_cs);

    if(ns && SUCCEEDED(hres))
        return ret;

210
    hres = get_protocol_cf(schema, schema_len, NULL, &cf);
211 212 213
    if(FAILED(hres))
        return NULL;

214 215 216 217
    hres = IClassFactory_QueryInterface(cf, &IID_IInternetProtocolInfo, (void**)&ret);
    if(FAILED(hres))
        IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
    IClassFactory_Release(cf);
218 219 220 221

    return ret;
}

222
HRESULT get_protocol_handler(IUri *uri, CLSID *clsid, BOOL *urlmon_protocol, IClassFactory **ret)
223
{
224
    name_space *ns;
225
    BSTR scheme;
226 227
    HRESULT hres;

228 229
    *ret = NULL;

230 231 232 233
    /* FIXME: Avoid GetSchemeName call for known schemes */
    hres = IUri_GetSchemeName(uri, &scheme);
    if(FAILED(hres))
        return hres;
234

235 236
    EnterCriticalSection(&session_cs);

237
    ns = find_name_space(scheme);
238 239
    if(ns) {
        *ret = ns->cf;
240
        IClassFactory_AddRef(*ret);
241 242
        if(clsid)
            *clsid = ns->clsid;
243 244
        if(urlmon_protocol)
            *urlmon_protocol = ns->urlmon;
245 246
    }

247 248
    LeaveCriticalSection(&session_cs);

249 250 251 252 253 254 255
    if(*ret) {
        hres = S_OK;
    }else {
        if(urlmon_protocol)
            *urlmon_protocol = FALSE;
        hres = get_protocol_cf(scheme, SysStringLen(scheme), clsid, ret);
    }
256

257 258
    SysFreeString(scheme);
    return hres;
259 260
}

261 262
IInternetProtocol *get_mime_filter(LPCWSTR mime)
{
263 264 265 266
    static const WCHAR filtersW[] = {'P','r','o','t','o','c','o','l','s',
        '\\','F','i','l','t','e','r',0 };
    static const WCHAR CLSIDW[] = {'C','L','S','I','D',0};

267 268 269
    IClassFactory *cf = NULL;
    IInternetProtocol *ret;
    mime_filter *iter;
270 271 272 273
    HKEY hlist, hfilter;
    WCHAR clsidw[64];
    CLSID clsid;
    DWORD res, type, size;
274 275 276 277
    HRESULT hres;

    EnterCriticalSection(&session_cs);

278
    LIST_FOR_EACH_ENTRY(iter, &mime_filter_list, mime_filter, entry) {
279 280 281 282 283 284 285 286
        if(!strcmpW(iter->mime, mime)) {
            cf = iter->cf;
            break;
        }
    }

    LeaveCriticalSection(&session_cs);

287 288 289 290 291 292 293 294 295 296 297 298 299
    if(cf) {
        hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&ret);
        if(FAILED(hres)) {
            WARN("CreateInstance failed: %08x\n", hres);
            return NULL;
        }

        return ret;
    }

    res = RegOpenKeyW(HKEY_CLASSES_ROOT, filtersW, &hlist);
    if(res != ERROR_SUCCESS) {
        TRACE("Could not open MIME filters key\n");
300
        return NULL;
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
    }

    res = RegOpenKeyW(hlist, mime, &hfilter);
    CloseHandle(hlist);
    if(res != ERROR_SUCCESS)
        return NULL;

    size = sizeof(clsidw);
    res = RegQueryValueExW(hfilter, CLSIDW, NULL, &type, (LPBYTE)clsidw, &size);
    CloseHandle(hfilter);
    if(res!=ERROR_SUCCESS || type!=REG_SZ) {
        WARN("Could not get filter CLSID for %s\n", debugstr_w(mime));
        return NULL;
    }

    hres = CLSIDFromString(clsidw, &clsid);
    if(FAILED(hres)) {
        WARN("CLSIDFromString failed for %s (%x)\n", debugstr_w(mime), hres);
        return NULL;
    }
321

322
    hres = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void**)&ret);
323
    if(FAILED(hres)) {
324
        WARN("CoCreateInstance failed: %08x\n", hres);
325 326 327 328 329 330
        return NULL;
    }

    return ret;
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
static HRESULT WINAPI InternetSession_QueryInterface(IInternetSession *iface,
        REFIID riid, void **ppv)
{
    TRACE("(%s %p)\n", debugstr_guid(riid), ppv);

    if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetSession, riid)) {
        *ppv = iface;
        IInternetSession_AddRef(iface);
        return S_OK;
    }

    *ppv = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI InternetSession_AddRef(IInternetSession *iface)
{
    TRACE("()\n");
    URLMON_LockModule();
    return 2;
}

static ULONG WINAPI InternetSession_Release(IInternetSession *iface)
{
    TRACE("()\n");
    URLMON_UnlockModule();
    return 1;
}

static HRESULT WINAPI InternetSession_RegisterNameSpace(IInternetSession *iface,
        IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzProtocol, ULONG cPatterns,
        const LPCWSTR *ppwzPatterns, DWORD dwReserved)
{
364
    TRACE("(%p %s %s %d %p %d)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzProtocol),
365 366 367 368 369
          cPatterns, ppwzPatterns, dwReserved);

    if(cPatterns || ppwzPatterns)
        FIXME("patterns not supported\n");
    if(dwReserved)
370
        WARN("dwReserved = %d\n", dwReserved);
371 372 373 374

    if(!pCF || !pwzProtocol)
        return E_INVALIDARG;

375
    return register_namespace(pCF, rclsid, pwzProtocol, FALSE);
376 377 378 379 380
}

static HRESULT WINAPI InternetSession_UnregisterNameSpace(IInternetSession *iface,
        IClassFactory *pCF, LPCWSTR pszProtocol)
{
381 382 383 384 385
    TRACE("(%p %s)\n", pCF, debugstr_w(pszProtocol));

    if(!pCF || !pszProtocol)
        return E_INVALIDARG;

386
    return unregister_namespace(pCF, pszProtocol);
387 388 389 390 391
}

static HRESULT WINAPI InternetSession_RegisterMimeFilter(IInternetSession *iface,
        IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType)
{
392 393 394 395 396 397 398 399 400 401 402
    mime_filter *filter;

    TRACE("(%p %s %s)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzType));

    filter = heap_alloc(sizeof(mime_filter));

    IClassFactory_AddRef(pCF);
    filter->cf = pCF;
    filter->clsid = *rclsid;
    filter->mime = heap_strdupW(pwzType);

403 404
    EnterCriticalSection(&session_cs);

405
    list_add_head(&mime_filter_list, &filter->entry);
406

407 408
    LeaveCriticalSection(&session_cs);

409
    return S_OK;
410 411 412 413 414
}

static HRESULT WINAPI InternetSession_UnregisterMimeFilter(IInternetSession *iface,
        IClassFactory *pCF, LPCWSTR pwzType)
{
415
    mime_filter *iter;
416 417 418

    TRACE("(%p %s)\n", pCF, debugstr_w(pwzType));

419 420
    EnterCriticalSection(&session_cs);

421 422 423
    LIST_FOR_EACH_ENTRY(iter, &mime_filter_list, mime_filter, entry) {
        if(iter->cf == pCF && !strcmpW(iter->mime, pwzType)) {
            list_remove(&iter->entry);
424

425
            LeaveCriticalSection(&session_cs);
426

427 428 429 430 431
            IClassFactory_Release(iter->cf);
            heap_free(iter->mime);
            heap_free(iter);
            return S_OK;
        }
432
    }
433

434
    LeaveCriticalSection(&session_cs);
435
    return S_OK;
436 437 438 439
}

static HRESULT WINAPI InternetSession_CreateBinding(IInternetSession *iface,
        LPBC pBC, LPCWSTR szUrl, IUnknown *pUnkOuter, IUnknown **ppUnk,
440
        IInternetProtocol **ppOInetProt, DWORD dwOption)
441
{
442
    BindProtocol *protocol;
443 444
    HRESULT hres;

445
    TRACE("(%p %s %p %p %p %08x)\n", pBC, debugstr_w(szUrl), pUnkOuter, ppUnk,
446
            ppOInetProt, dwOption);
447 448 449 450

    if(pBC || pUnkOuter || ppUnk || dwOption)
        FIXME("Unsupported arguments\n");

451 452 453 454
    hres = create_binding_protocol(FALSE, &protocol);
    if(FAILED(hres))
        return hres;

455
    *ppOInetProt = (IInternetProtocol*)&protocol->IInternetProtocolEx_iface;
456
    return S_OK;
457 458 459 460 461
}

static HRESULT WINAPI InternetSession_SetSessionOption(IInternetSession *iface,
        DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength, DWORD dwReserved)
{
462
    FIXME("(%08x %p %d %d)\n", dwOption, pBuffer, dwBufferLength, dwReserved);
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    return E_NOTIMPL;
}

static const IInternetSessionVtbl InternetSessionVtbl = {
    InternetSession_QueryInterface,
    InternetSession_AddRef,
    InternetSession_Release,
    InternetSession_RegisterNameSpace,
    InternetSession_UnregisterNameSpace,
    InternetSession_RegisterMimeFilter,
    InternetSession_UnregisterMimeFilter,
    InternetSession_CreateBinding,
    InternetSession_SetSessionOption
};

static IInternetSession InternetSession = { &InternetSessionVtbl };

/***********************************************************************
 *           CoInternetGetSession (URLMON.@)
 *
 * Create a new internet session and return an IInternetSession interface
 * representing it.
 *
 * PARAMS
 *    dwSessionMode      [I] Mode for the internet session
 *    ppIInternetSession [O] Destination for creates IInternetSession object
 *    dwReserved         [I] Reserved, must be 0.
 *
 * RETURNS
 *    Success: S_OK. ppIInternetSession contains the IInternetSession interface.
 *    Failure: E_INVALIDARG, if any argument is invalid, or
 *             E_OUTOFMEMORY if memory allocation fails.
 */
HRESULT WINAPI CoInternetGetSession(DWORD dwSessionMode, IInternetSession **ppIInternetSession,
        DWORD dwReserved)
{
499
    TRACE("(%d %p %d)\n", dwSessionMode, ppIInternetSession, dwReserved);
500 501

    if(dwSessionMode)
502
        ERR("dwSessionMode=%d\n", dwSessionMode);
503
    if(dwReserved)
504
        ERR("dwReserved=%d\n", dwReserved);
505

506
    IInternetSession_AddRef(&InternetSession);
507 508 509
    *ppIInternetSession = &InternetSession;
    return S_OK;
}
510 511 512 513 514 515 516 517 518 519 520

/**************************************************************************
 *                 UrlMkGetSessionOption (URLMON.@)
 */
static BOOL get_url_encoding(HKEY root, DWORD *encoding)
{
    DWORD size = sizeof(DWORD), res, type;
    HKEY hkey;

    static const WCHAR wszUrlEncoding[] = {'U','r','l','E','n','c','o','d','i','n','g',0};

521
    res = RegOpenKeyW(root, internet_settings_keyW, &hkey);
522 523 524 525 526 527 528 529 530
    if(res != ERROR_SUCCESS)
        return FALSE;

    res = RegQueryValueExW(hkey, wszUrlEncoding, NULL, &type, (LPBYTE)encoding, &size);
    RegCloseKey(hkey);

    return res == ERROR_SUCCESS;
}

531 532 533 534
static LPWSTR user_agent;

static void ensure_useragent(void)
{
535 536
    OSVERSIONINFOW info = {sizeof(info)};
    const WCHAR *os_type, *is_nt;
537 538 539
    WCHAR buf[512], *ret, *tmp;
    DWORD res, idx=0;
    size_t len, size;
540
    BOOL is_wow;
541
    HKEY key;
542 543 544 545 546 547

    static const WCHAR formatW[] =
        {'M','o','z','i','l','l','a','/','4','.','0',
         ' ','(','c','o','m','p','a','t','i','b','l','e',';',
         ' ','M','S','I','E',' ','8','.','0',';',
         ' ','W','i','n','d','o','w','s',' ','%','s','%','d','.','%','d',';',
548 549 550 551 552 553 554 555 556
         ' ','%','s',';',' ','T','r','i','d','e','n','t','/','5','.','0',0};
    static const WCHAR post_platform_keyW[] =
        {'S','O','F','T','W','A','R','E',
         '\\','M','i','c','r','o','s','o','f','t',
         '\\','W','i','n','d','o','w','s',
         '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
         '\\','I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',
         '\\','5','.','0','\\','U','s','e','r',' ','A','g','e','n','t',
         '\\','P','o','s','t',' ','P','l','a','t','f','o','r','m',0};
557 558 559 560 561
    static const WCHAR ntW[] = {'N','T',' ',0};
    static const WCHAR win32W[] = {'W','i','n','3','2',0};
    static const WCHAR win64W[] = {'W','i','n','6','4',0};
    static const WCHAR wow64W[] = {'W','O','W','6','4',0};
    static const WCHAR emptyW[] = {0};
562 563 564 565

    if(user_agent)
        return;

566 567
    GetVersionExW(&info);
    is_nt = info.dwPlatformId == VER_PLATFORM_WIN32_NT ? ntW : emptyW;
568

569 570 571 572 573 574
    if(sizeof(void*) == 8)
        os_type = win64W;
    else if(IsWow64Process(GetCurrentProcess(), &is_wow) && is_wow)
        os_type = wow64W;
    else
        os_type = win32W;
575

576
    sprintfW(buf, formatW, is_nt, info.dwMajorVersion, info.dwMinorVersion, os_type);
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
    len = strlenW(buf);

    size = len+40;
    ret = heap_alloc(size * sizeof(WCHAR));
    if(!ret)
        return;

    memcpy(ret, buf, len*sizeof(WCHAR));

    res = RegOpenKeyW(HKEY_LOCAL_MACHINE, post_platform_keyW, &key);
    if(res == ERROR_SUCCESS) {
        DWORD value_len;

        while(1) {
            value_len = sizeof(buf)/sizeof(WCHAR);
            res = RegEnumValueW(key, idx, buf, &value_len, NULL, NULL, NULL, NULL);
            if(res != ERROR_SUCCESS)
                break;
            idx++;

            if(len + value_len + 2 /* strlen("; ") */ + 1 /* trailing ')' */ >= size) {
                tmp = heap_realloc(ret, (size*2+value_len)*sizeof(WCHAR));
                if(!tmp)
                    break;
                ret = tmp;
                size = size*2+value_len;
            }

            ret[len++] = ';';
            ret[len++] = ' ';
            memcpy(ret+len, buf, value_len*sizeof(WCHAR));
            len += value_len;
        }

        RegCloseKey(key);
    }

    ret[len++] = ')';
    ret[len++] = 0;

    user_agent = ret;
    TRACE("Using user agent %s\n", debugstr_w(user_agent));
619 620
}

621 622 623 624 625 626 627 628 629 630 631 632 633
LPWSTR get_useragent(void)
{
    LPWSTR ret;

    ensure_useragent();

    EnterCriticalSection(&session_cs);
    ret = heap_strdupW(user_agent);
    LeaveCriticalSection(&session_cs);

    return ret;
}

634 635 636
HRESULT WINAPI UrlMkGetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
                                     DWORD* pdwBufferLength, DWORD dwReserved)
{
637
    TRACE("(%x, %p, %d, %p)\n", dwOption, pBuffer, dwBufferLength, pdwBufferLength);
638 639

    if(dwReserved)
640
        WARN("dwReserved = %d\n", dwReserved);
641 642

    switch(dwOption) {
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    case URLMON_OPTION_USERAGENT: {
        HRESULT hres = E_OUTOFMEMORY;
        DWORD size;

        if(!pdwBufferLength)
            return E_INVALIDARG;

        EnterCriticalSection(&session_cs);

        ensure_useragent();
        if(user_agent) {
            size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
            *pdwBufferLength = size;
            if(size <= dwBufferLength) {
                if(pBuffer)
                    WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pBuffer, size, NULL, NULL);
                else
                    hres = E_INVALIDARG;
            }
        }

        LeaveCriticalSection(&session_cs);

        /* Tests prove that we have to return E_OUTOFMEMORY on success. */
        return hres;
    }
669 670 671 672 673 674 675 676 677 678 679 680 681 682
    case URLMON_OPTION_URL_ENCODING: {
        DWORD encoding = 0;

        if(!pBuffer || dwBufferLength < sizeof(DWORD) || !pdwBufferLength)
            return E_INVALIDARG;

        if(!get_url_encoding(HKEY_CURRENT_USER, &encoding))
            get_url_encoding(HKEY_LOCAL_MACHINE, &encoding);

        *pdwBufferLength = sizeof(DWORD);
        *(DWORD*)pBuffer = encoding ? URL_ENCODING_DISABLE_UTF8 : URL_ENCODING_ENABLE_UTF8;
        return S_OK;
    }
    default:
683
        FIXME("unsupported option %x\n", dwOption);
684 685 686 687
    }

    return E_INVALIDARG;
}
688

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
/**************************************************************************
 *                 UrlMkSetSessionOption (URLMON.@)
 */
HRESULT WINAPI UrlMkSetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
        DWORD Reserved)
{
    TRACE("(%x %p %x)\n", dwOption, pBuffer, dwBufferLength);

    switch(dwOption) {
    case URLMON_OPTION_USERAGENT: {
        LPWSTR new_user_agent;
        char *buf = pBuffer;
        DWORD len, size;

        if(!pBuffer || !dwBufferLength)
            return E_INVALIDARG;

        for(len=0; len<dwBufferLength && buf[len]; len++);

        TRACE("Setting user agent %s\n", debugstr_an(buf, len));

        size = MultiByteToWideChar(CP_ACP, 0, buf, len, NULL, 0);
        new_user_agent = heap_alloc((size+1)*sizeof(WCHAR));
        if(!new_user_agent)
            return E_OUTOFMEMORY;
        MultiByteToWideChar(CP_ACP, 0, buf, len, new_user_agent, size);
        new_user_agent[size] = 0;

        EnterCriticalSection(&session_cs);

        heap_free(user_agent);
        user_agent = new_user_agent;
721
        update_user_agent(user_agent);
722 723 724 725 726 727 728 729 730 731 732 733

        LeaveCriticalSection(&session_cs);
        break;
    }
    default:
        FIXME("Unknown option %x\n", dwOption);
        return E_INVALIDARG;
    }

    return S_OK;
}

734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
/**************************************************************************
 *                 ObtainUserAgentString (URLMON.@)
 */
HRESULT WINAPI ObtainUserAgentString(DWORD dwOption, LPSTR pcszUAOut, DWORD *cbSize)
{
    DWORD size;
    HRESULT hres = E_FAIL;

    TRACE("(%d %p %p)\n", dwOption, pcszUAOut, cbSize);

    if(!pcszUAOut || !cbSize)
        return E_INVALIDARG;

    EnterCriticalSection(&session_cs);

    ensure_useragent();
    if(user_agent) {
        size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);

        if(size <= *cbSize) {
            WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pcszUAOut, *cbSize, NULL, NULL);
            hres = S_OK;
        }else {
            hres = E_OUTOFMEMORY;
        }

        *cbSize = size;
    }

    LeaveCriticalSection(&session_cs);
    return hres;
}

767 768
void free_session(void)
{
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
    name_space *ns_iter, *ns_last;
    mime_filter *mf_iter, *mf_last;

    LIST_FOR_EACH_ENTRY_SAFE(ns_iter, ns_last, &name_space_list, name_space, entry) {
            if(!ns_iter->urlmon)
                IClassFactory_Release(ns_iter->cf);
            heap_free(ns_iter->protocol);
            heap_free(ns_iter);
    }

    LIST_FOR_EACH_ENTRY_SAFE(mf_iter, mf_last, &mime_filter_list, mime_filter, entry) {
            IClassFactory_Release(mf_iter->cf);
            heap_free(mf_iter->mime);
            heap_free(mf_iter);
    }

785 786
    heap_free(user_agent);
}