Correct bug truncating downloaded files to 4096 bytes.
[wine] / dlls / urlmon / umon.c
1 /*
2  * UrlMon
3  *
4  * Copyright 1999 Ulrich Czekalla for Corel Corporation
5  * Copyright 2002 Huw D M Davies for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define COM_NO_WINDOWS_H
23 #include <stdarg.h>
24 #include <stdio.h>
25
26 #define COBJMACROS
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "winuser.h"
35 #include "objbase.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "ole2.h"
39 #include "urlmon.h"
40 #include "wininet.h"
41 #include "shlwapi.h"
42 #include "urlmon_main.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
45
46 /* native urlmon.dll uses this key, too */
47 static const WCHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
48
49 /*static BOOL registered_wndclass = FALSE;*/
50
51 /* filemoniker data structure */
52 typedef struct URLMonikerImpl{
53
54     IMonikerVtbl*  lpvtbl1;  /* VTable relative to the IMoniker interface.*/
55     IBindingVtbl*  lpvtbl2;  /* VTable to IBinding interface */
56
57     ULONG ref; /* reference counter for this object */
58
59     LPOLESTR URLName; /* URL string identified by this URLmoniker */
60
61     HWND hwndCallback;
62     IBindCtx *pBC;
63     HINTERNET hinternet, hconnect, hrequest;
64     HANDLE hCacheFile;
65     IUMCacheStream *pstrCache;
66     IBindStatusCallback *pbscb;
67     DWORD total_read, expected_size;
68 } URLMonikerImpl;
69
70 /*******************************************************************************
71  *        URLMoniker_QueryInterface
72  *******************************************************************************/
73 static HRESULT WINAPI URLMonikerImpl_QueryInterface(IMoniker* iface,REFIID riid,void** ppvObject)
74 {
75     URLMonikerImpl *This = (URLMonikerImpl *)iface;
76
77     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppvObject);
78
79     /* Perform a sanity check on the parameters.*/
80     if ( (This==0) || (ppvObject==0) )
81         return E_INVALIDARG;
82
83     /* Initialize the return parameter */
84     *ppvObject = 0;
85
86     /* Compare the riid with the interface IDs implemented by this object.*/
87     if (IsEqualIID(&IID_IUnknown, riid)      ||
88         IsEqualIID(&IID_IPersist, riid)      ||
89         IsEqualIID(&IID_IPersistStream,riid) ||
90         IsEqualIID(&IID_IMoniker, riid)
91        )
92         *ppvObject = iface;
93
94     /* Check that we obtained an interface.*/
95     if ((*ppvObject)==0)
96         return E_NOINTERFACE;
97
98     /* Query Interface always increases the reference count by one when it is successful */
99     IMoniker_AddRef(iface);
100
101     return S_OK;
102 }
103
104 /******************************************************************************
105  *        URLMoniker_AddRef
106  ******************************************************************************/
107 static ULONG WINAPI URLMonikerImpl_AddRef(IMoniker* iface)
108 {
109     URLMonikerImpl *This = (URLMonikerImpl *)iface;
110     ULONG refCount = InterlockedIncrement(&This->ref);
111
112     TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
113
114     URLMON_LockModule();
115
116     return refCount;
117 }
118
119 /******************************************************************************
120  *        URLMoniker_Release
121  ******************************************************************************/
122 static ULONG WINAPI URLMonikerImpl_Release(IMoniker* iface)
123 {
124     URLMonikerImpl *This = (URLMonikerImpl *)iface;
125     ULONG refCount = InterlockedDecrement(&This->ref);
126
127     TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
128
129     /* destroy the object if there's no more reference on it */
130     if (!refCount) {
131         HeapFree(GetProcessHeap(),0,This->URLName);
132         HeapFree(GetProcessHeap(),0,This);
133         if (This->hCacheFile)
134             CloseHandle(This->hCacheFile);
135         if (This->pstrCache)
136         {
137             UMCloseCacheFileStream(This->pstrCache);
138             IStream_Release((IStream *)This->pstrCache);
139         }
140         if (This->pbscb)
141                 IBindStatusCallback_Release(This->pbscb);
142     }
143
144     URLMON_UnlockModule();
145
146     return refCount;
147 }
148
149 static void URLMonikerImpl_CloseCacheDownload(URLMonikerImpl *This)
150 {
151     CloseHandle(This->hCacheFile);
152     This->hCacheFile = 0;
153     UMCloseCacheFileStream(This->pstrCache);
154     IStream_Release((IStream *)This->pstrCache);
155     This->pstrCache = 0;
156 }
157
158 static HRESULT URLMonikerImpl_MoreCacheData(URLMonikerImpl *This, char *buf, DWORD dwBytes)
159 {
160     DWORD written;
161
162     if (WriteFile(This->hCacheFile, buf, dwBytes, &written, NULL) && written == dwBytes)
163     {
164         HRESULT hr;
165
166         This->total_read += written;
167         hr = IBindStatusCallback_OnProgress(This->pbscb,
168                                             This->total_read + written,
169                                             This->expected_size,
170                                             (This->total_read == written) ?
171                                                 BINDSTATUS_BEGINDOWNLOADDATA :
172                                                 BINDSTATUS_DOWNLOADINGDATA,
173                                             NULL);
174         if (!hr)
175         {
176             STGMEDIUM stg;
177             FORMATETC fmt;
178
179             fmt.cfFormat = 0;
180             fmt.ptd = NULL;
181             fmt.dwAspect = 0;
182             fmt.lindex = -1;
183             fmt.tymed = TYMED_ISTREAM;
184
185             stg.tymed = TYMED_ISTREAM;
186             stg.u.pstm = (IStream *)This->pstrCache;
187             stg.pUnkForRelease = NULL;
188
189             hr = IBindStatusCallback_OnDataAvailable(This->pbscb,
190                                                      (This->total_read == written) ?
191                                                          BSCF_FIRSTDATANOTIFICATION :
192                                                          BSCF_INTERMEDIATEDATANOTIFICATION,
193                                                      This->total_read + written,
194                                                      &fmt,
195                                                      &stg);
196         }
197         if (written < dwBytes)
198             return STG_E_MEDIUMFULL;
199         else
200             return hr;
201     }
202     return HRESULT_FROM_WIN32(GetLastError());
203 }
204
205 static void URLMonikerImpl_FinishedDownload(URLMonikerImpl *This, HRESULT hr)
206 {
207     STGMEDIUM stg;
208     FORMATETC fmt;
209
210     fmt.ptd = NULL;
211     fmt.dwAspect = 0;
212     fmt.lindex = -1;
213     fmt.tymed = TYMED_ISTREAM;
214
215     stg.tymed = TYMED_ISTREAM;
216     stg.u.pstm = (IStream *)This->pstrCache;
217     stg.pUnkForRelease = NULL;
218
219     IBindStatusCallback_OnProgress(This->pbscb, This->total_read, This->expected_size, BINDSTATUS_ENDDOWNLOADDATA, NULL);
220     IBindStatusCallback_OnDataAvailable(This->pbscb, BSCF_LASTDATANOTIFICATION, This->total_read, &fmt, &stg);
221     if (hr)
222     {
223         WCHAR *pwchError = 0;
224
225         FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
226                          FORMAT_MESSAGE_ALLOCATE_BUFFER,
227                         NULL, (DWORD) hr,
228                         0, (LPWSTR) &pwchError,
229                         0, NULL);
230         if (!pwchError)
231         {
232             static WCHAR achFormat[] = { '%', '0', '8', 'x', 0 };
233
234             pwchError =(WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * 9);
235             wsprintfW(pwchError, achFormat, hr);
236         }
237         IBindStatusCallback_OnStopBinding(This->pbscb, hr, pwchError);
238         LocalFree(pwchError);
239     }
240     else
241     {
242         IBindStatusCallback_OnStopBinding(This->pbscb, hr, NULL);
243     }
244     IBindStatusCallback_Release(This->pbscb);
245     This->pbscb = 0;
246 }
247
248 /******************************************************************************
249  *        URLMoniker_GetClassID
250  ******************************************************************************/
251 static HRESULT WINAPI URLMonikerImpl_GetClassID(IMoniker* iface,
252                                                 CLSID *pClassID)/* Pointer to CLSID of object */
253 {
254     URLMonikerImpl *This = (URLMonikerImpl *)iface;
255
256     TRACE("(%p,%p)\n",This,pClassID);
257
258     if (pClassID==NULL)
259         return E_POINTER;
260     /* Windows always returns CLSID_StdURLMoniker */
261     *pClassID = CLSID_StdURLMoniker;
262     return S_OK;
263 }
264
265 /******************************************************************************
266  *        URLMoniker_IsDirty
267  ******************************************************************************/
268 static HRESULT WINAPI URLMonikerImpl_IsDirty(IMoniker* iface)
269 {
270     URLMonikerImpl *This = (URLMonikerImpl *)iface;
271     /* Note that the OLE-provided implementations of the IPersistStream::IsDirty
272        method in the OLE-provided moniker interfaces always return S_FALSE because
273        their internal state never changes. */
274
275     TRACE("(%p)\n",This);
276
277     return S_FALSE;
278 }
279
280 /******************************************************************************
281  *        URLMoniker_Load
282  *
283  * NOTE
284  *  Writes a ULONG containing length of unicode string, followed
285  *  by that many unicode characters
286  ******************************************************************************/
287 static HRESULT WINAPI URLMonikerImpl_Load(IMoniker* iface,IStream* pStm)
288 {
289     URLMonikerImpl *This = (URLMonikerImpl *)iface;
290     
291     HRESULT res;
292     ULONG len;
293     ULONG got;
294     TRACE("(%p,%p)\n",This,pStm);
295
296     if(!pStm)
297         return E_INVALIDARG;
298
299     res = IStream_Read(pStm, &len, sizeof(ULONG), &got);
300     if(SUCCEEDED(res)) {
301         if(got == sizeof(ULONG)) {
302             HeapFree(GetProcessHeap(), 0, This->URLName);
303             This->URLName=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(len+1));
304             if(!This->URLName)
305                 res = E_OUTOFMEMORY;
306             else {
307                 res = IStream_Read(pStm, This->URLName, len, NULL);
308                 This->URLName[len] = 0;
309             }
310         }
311         else
312             res = E_FAIL;
313     }
314     return res;
315 }
316
317 /******************************************************************************
318  *        URLMoniker_Save
319  ******************************************************************************/
320 static HRESULT WINAPI URLMonikerImpl_Save(IMoniker* iface,
321                                           IStream* pStm,/* pointer to the stream where the object is to be saved */
322                                           BOOL fClearDirty)/* Specifies whether to clear the dirty flag */
323 {
324     URLMonikerImpl *This = (URLMonikerImpl *)iface;
325
326     HRESULT res;
327     ULONG len;
328     TRACE("(%p,%p,%d)\n",This,pStm,fClearDirty);
329
330     if(!pStm)
331         return E_INVALIDARG;
332
333     len = strlenW(This->URLName);
334     res=IStream_Write(pStm,&len,sizeof(ULONG),NULL);
335     if(SUCCEEDED(res))
336         res=IStream_Write(pStm,&This->URLName,len*sizeof(WCHAR),NULL);
337     return res;
338
339 }
340
341 /******************************************************************************
342  *        URLMoniker_GetSizeMax
343  ******************************************************************************/
344 static HRESULT WINAPI URLMonikerImpl_GetSizeMax(IMoniker* iface,
345                                                 ULARGE_INTEGER* pcbSize)/* Pointer to size of stream needed to save object */
346 {
347     URLMonikerImpl *This = (URLMonikerImpl *)iface;
348
349     TRACE("(%p,%p)\n",This,pcbSize);
350
351     if(!pcbSize)
352         return E_INVALIDARG;
353
354     pcbSize->u.LowPart = sizeof(ULONG) + (strlenW(This->URLName) * sizeof(WCHAR));
355     pcbSize->u.HighPart = 0;
356     return S_OK;
357 }
358
359 /******************************************************************************
360  *                  URLMoniker_BindToObject
361  ******************************************************************************/
362 static HRESULT WINAPI URLMonikerImpl_BindToObject(IMoniker* iface,
363                                                   IBindCtx* pbc,
364                                                   IMoniker* pmkToLeft,
365                                                   REFIID riid,
366                                                   VOID** ppvResult)
367 {
368     URLMonikerImpl *This = (URLMonikerImpl *)iface;
369
370     *ppvResult=0;
371
372     FIXME("(%p)->(%p,%p,%s,%p): stub\n",This,pbc,pmkToLeft,debugstr_guid(riid),
373           ppvResult);
374
375     return E_NOTIMPL;
376 }
377
378 typedef struct {
379     enum {OnProgress, OnDataAvailable} callback;
380 } URLMON_CallbackData;
381
382
383 #if 0
384 static LRESULT CALLBACK URLMON_WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
385 {
386     return DefWindowProcA(hwnd, msg, wparam, lparam);
387 }
388
389 static void PostOnProgress(URLMonikerImpl *This, UINT progress, UINT maxprogress, DWORD status, LPCWSTR *str)
390 {
391 }
392
393 static void CALLBACK URLMON_InternetCallback(HINTERNET hinet, /*DWORD_PTR*/ DWORD context, DWORD status,
394                                              void *status_info, DWORD status_info_len)
395 {
396     URLMonikerImpl *This = (URLMonikerImpl *)context;
397     TRACE("handle %p this %p status %08lx\n", hinet, This, status);
398
399     if(This->filesize == -1) {
400         switch(status) {
401         case INTERNET_STATUS_RESOLVING_NAME:
402             PostOnProgess(This, 0, 0, BINDSTATUS_FINDINGRESOURCE, status_info);
403             break;
404         case INTERNET_STATUS_CONNECTING_TO_SERVER:
405             PostOnProgress(This, 0, 0, BINDSTATUS_CONNECTING, NULL);
406             break;
407         case INTERNET_STATUS_SENDING_REQUEST:
408             PostOnProgress(This, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL);
409             break;
410         case INTERNET_REQUEST_COMPLETE:
411           {
412               DWORD len, lensz = sizeof(len);
413
414             HttpQueryInfoW(hrequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &len, &lensz, NULL);
415             TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), len);
416             This->filesize = len;
417             break;
418           }
419         }
420     }
421
422     return;
423 }
424 #endif
425
426
427 /******************************************************************************
428  *        URLMoniker_BindToStorage
429  ******************************************************************************/
430 static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface,
431                                                    IBindCtx* pbc,
432                                                    IMoniker* pmkToLeft,
433                                                    REFIID riid,
434                                                    VOID** ppvObject)
435 {
436     URLMonikerImpl *This = (URLMonikerImpl *)iface;
437     HRESULT hres;
438     BINDINFO bi;
439     DWORD bindf;
440     WCHAR szFileName[MAX_PATH + 1];
441
442     if(pmkToLeft) {
443         FIXME("pmkToLeft != NULL\n");
444         return E_NOTIMPL;
445     }
446     if(!IsEqualIID(&IID_IStream, riid)) {
447         FIXME("unsupported iid\n");
448         return E_NOTIMPL;
449     }
450
451     hres = UMCreateStreamOnCacheFile(This->URLName, 0, szFileName, &This->hCacheFile, &This->pstrCache);
452
453     if(SUCCEEDED(hres)) {
454         TRACE("Created stream...\n");
455
456         *ppvObject = (void *) This->pstrCache;
457         IStream_AddRef((IStream *) This->pstrCache);
458
459         hres = IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown**)&This->pbscb);
460         if(SUCCEEDED(hres)) {
461             TRACE("Got IBindStatusCallback...\n");
462
463             memset(&bi, 0, sizeof(bi));
464             bi.cbSize = sizeof(bi);
465             bindf = 0;
466             hres = IBindStatusCallback_GetBindInfo(This->pbscb, &bindf, &bi);
467             if(SUCCEEDED(hres)) {
468                 WCHAR *urlcopy, *tmpwc;
469                 URL_COMPONENTSW url;
470                 WCHAR *host, *path, *user, *pass;
471                 DWORD lensz = sizeof(This->expected_size);
472                 DWORD dwService = 0;
473                 BOOL bSuccess;
474
475                 TRACE("got bindinfo. bindf = %08lx extrainfo = %s bindinfof = %08lx bindverb = %08lx iid %s\n",
476                       bindf, debugstr_w(bi.szExtraInfo), bi.grfBindInfoF, bi.dwBindVerb, debugstr_guid(&bi.iid));
477                 hres = IBindStatusCallback_OnStartBinding(This->pbscb, 0, (IBinding*)&This->lpvtbl2);
478                 TRACE("OnStartBinding rets %08lx\n", hres);
479
480                 /* This class will accept URLs with the backslash in them. But InternetCrackURL will not - it
481                  * requires forward slashes (this is the behaviour of Microsoft's INETAPI). So we need to make
482                  * a copy of the URL here and change the backslash to a forward slash everywhere it appears -
483                  * but only before any '#' or '?', after which backslash should be left alone.
484                  */
485                 urlcopy = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(This->URLName) + 1));
486                 lstrcpyW(urlcopy, This->URLName);
487                 for (tmpwc = urlcopy; *tmpwc && *tmpwc != '#' && *tmpwc != '?'; ++tmpwc)
488                     if (*tmpwc == '\\')
489                             *tmpwc = '/';
490
491 #if 0
492                 if(!registered_wndclass) {
493                     WNDCLASSA urlmon_wndclass = {0, URLMON_WndProc,0, 0, URLMON_hInstance, 0, 0, 0, NULL, "URLMON_Callback_Window_Class"};
494                     RegisterClassA(&urlmon_wndclass);
495                     registered_wndclass = TRUE;
496                 }
497
498                 This->hwndCallback = CreateWindowA("URLMON_Callback_Window_Class", NULL, 0, 0, 0, 0, 0, 0, 0,
499                                                    URLMON_hInstance, NULL);
500
501 #endif
502                 This->expected_size = 0;
503                 This->total_read = 0;
504
505                 memset(&url, 0, sizeof(url));
506                 url.dwStructSize = sizeof(url);
507                 url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength = url.dwPasswordLength = 1;
508                 InternetCrackUrlW(urlcopy, 0, 0, &url);
509                 host = HeapAlloc(GetProcessHeap(), 0, (url.dwHostNameLength + 1) * sizeof(WCHAR));
510                 memcpy(host, url.lpszHostName, url.dwHostNameLength * sizeof(WCHAR));
511                 host[url.dwHostNameLength] = '\0';
512                 path = HeapAlloc(GetProcessHeap(), 0, (url.dwUrlPathLength + 1) * sizeof(WCHAR));
513                 memcpy(path, url.lpszUrlPath, url.dwUrlPathLength * sizeof(WCHAR));
514                 path[url.dwUrlPathLength] = '\0';
515                 if (url.dwUserNameLength)
516                 {
517                     user = HeapAlloc(GetProcessHeap(), 0, ((url.dwUserNameLength + 1) * sizeof(WCHAR)));
518                     memcpy(user, url.lpszUserName, url.dwUserNameLength * sizeof(WCHAR));
519                     user[url.dwUserNameLength] = 0;
520                 }
521                 else
522                 {
523                     user = 0;
524                 }
525                 if (url.dwPasswordLength)
526                 {
527                     pass = HeapAlloc(GetProcessHeap(), 0, ((url.dwPasswordLength + 1) * sizeof(WCHAR)));
528                     memcpy(pass, url.lpszPassword, url.dwPasswordLength * sizeof(WCHAR));
529                     pass[url.dwPasswordLength] = 0;
530                 }
531                 else
532                 {
533                     pass = 0;
534                 }
535
536                 switch ((DWORD) url.nScheme)
537                 {
538                 case INTERNET_SCHEME_FTP:
539                 case INTERNET_SCHEME_GOPHER:
540                 case INTERNET_SCHEME_HTTP:
541                 case INTERNET_SCHEME_HTTPS:
542
543                     This->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0 /*INTERNET_FLAG_ASYNC*/);
544 /*                  InternetSetStatusCallback(This->hinternet, URLMON_InternetCallback);*/
545                     if (!This->hinternet)
546                     {
547                             hres = HRESULT_FROM_WIN32(GetLastError());
548                             break;
549                     }
550
551                     switch ((DWORD) url.nScheme)
552                     {
553                     case INTERNET_SCHEME_FTP:
554                         if (!url.nPort)
555                             url.nPort = INTERNET_DEFAULT_FTP_PORT;
556                         dwService = INTERNET_SERVICE_FTP;
557                         break;
558     
559                     case INTERNET_SCHEME_GOPHER:
560                         if (!url.nPort)
561                             url.nPort = INTERNET_DEFAULT_GOPHER_PORT;
562                         dwService = INTERNET_SERVICE_GOPHER;
563                         break;
564
565                     case INTERNET_SCHEME_HTTP:
566                         if (!url.nPort)
567                             url.nPort = INTERNET_DEFAULT_HTTP_PORT;
568                         dwService = INTERNET_SERVICE_HTTP;
569                         break;
570
571                     case INTERNET_SCHEME_HTTPS:
572                         if (!url.nPort)
573                             url.nPort = INTERNET_DEFAULT_HTTPS_PORT;
574                         dwService = INTERNET_SERVICE_HTTP;
575                         break;
576                     }
577
578                     This->hconnect = InternetConnectW(This->hinternet, host, url.nPort, user, pass,
579                                                       dwService, 0, (DWORD)This);
580                     if (!This->hconnect)
581                     {
582                             hres = HRESULT_FROM_WIN32(GetLastError());
583                             CloseHandle(This->hinternet);
584                             break;
585                     }
586
587                     hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, 0x22, NULL);
588                     hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL);
589                     hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CONNECTING, NULL);
590                     hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL);
591
592                     bSuccess = FALSE;
593
594                     switch (dwService)
595                     {
596                     case INTERNET_SERVICE_GOPHER:
597                         This->hrequest = GopherOpenFileW(This->hconnect,
598                                                          path,
599                                                          0,
600                                                          INTERNET_FLAG_RELOAD,
601                                                          0);
602                         if (This->hrequest)
603                                 bSuccess = TRUE;
604                         else
605                                 hres = HRESULT_FROM_WIN32(GetLastError());
606                         break;
607
608                     case INTERNET_SERVICE_FTP:
609                         This->hrequest = FtpOpenFileW(This->hconnect,
610                                                       path,
611                                                       GENERIC_READ,
612                                                       FTP_TRANSFER_TYPE_BINARY |
613                                                        INTERNET_FLAG_TRANSFER_BINARY |
614                                                        INTERNET_FLAG_RELOAD,
615                                                       0);
616                         if (This->hrequest)
617                                 bSuccess = TRUE;
618                         else
619                                 hres = HRESULT_FROM_WIN32(GetLastError());
620                         break;
621
622                     case INTERNET_SERVICE_HTTP:
623                         This->hrequest = HttpOpenRequestW(This->hconnect, NULL, path, NULL, NULL, NULL, 0, (DWORD)This);
624                         if (!This->hrequest)
625                         {
626                                 hres = HRESULT_FROM_WIN32(GetLastError());
627                         }
628                         else if (!HttpSendRequestW(This->hrequest, NULL, 0, NULL, 0))
629                         {
630                                 hres = HRESULT_FROM_WIN32(GetLastError());
631                                 InternetCloseHandle(This->hrequest);
632                         }
633                         else
634                         {
635                                 HttpQueryInfoW(This->hrequest,
636                                                HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
637                                                &This->expected_size,
638                                                &lensz,
639                                                NULL);
640                                 bSuccess = TRUE;
641                         }
642                         break;
643                     }
644                     if(bSuccess)
645                     {
646                         TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), This->expected_size);
647
648                         IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName);
649
650                         while(1) {
651                             char buf[4096];
652                             DWORD bufread;
653                             if(InternetReadFile(This->hrequest, buf, sizeof(buf), &bufread)) {
654                                 TRACE("read %ld bytes %s...\n", bufread, debugstr_an(buf, 10));
655                                 if(bufread == 0) break;
656                                 hres = URLMonikerImpl_MoreCacheData(This, buf, bufread);
657                             } else
658                                 break;
659                         }
660                         InternetCloseHandle(This->hrequest);
661                             hres = S_OK;
662                     }
663             
664                     InternetCloseHandle(This->hconnect);
665                     InternetCloseHandle(This->hinternet);
666                     break;
667
668                 case INTERNET_SCHEME_FILE:
669                     path = This->URLName + 5; /* Skip the "file:" part */
670                     if ((path[0] != '/' && path[0] != '\\') ||
671                         (path[1] != '/' && path[1] != '\\'))
672                     {
673                         hres = E_FAIL;
674                     }
675                     else
676                     {
677                         HANDLE h;
678
679                         path += 2;
680                         if (path[0] == '/' || path[0] == '\\')
681                             ++path;
682                         h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
683                         if (h == (HANDLE) HFILE_ERROR)
684                         {
685                             hres = HRESULT_FROM_WIN32(GetLastError());
686                         }
687                         else
688                         {
689                             char buf[4096];
690                             DWORD bufread;
691
692                             IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName);
693
694                             while (ReadFile(h, buf, sizeof(buf), &bufread, NULL) && bufread > 0)
695                                 hres = URLMonikerImpl_MoreCacheData(This, buf, bufread);
696
697                             CloseHandle(h);
698                             hres = S_OK;
699                         }
700                     }
701                         
702                     break;
703
704                 default:
705                     FIXME("Unsupported URI scheme");
706                     break;
707                 }
708                 URLMonikerImpl_CloseCacheDownload(This);
709                 URLMonikerImpl_FinishedDownload(This, hres);
710
711                 if (user)
712                     HeapFree(GetProcessHeap(), 0, user);
713                 if (pass)
714                     HeapFree(GetProcessHeap(), 0, pass);
715                 HeapFree(GetProcessHeap(), 0, path);
716                 HeapFree(GetProcessHeap(), 0, host);
717                 HeapFree(GetProcessHeap(), 0, urlcopy);
718             }
719         }
720     }
721     return hres;
722 }
723
724 /******************************************************************************
725  *        URLMoniker_Reduce
726  ******************************************************************************/
727 static HRESULT WINAPI URLMonikerImpl_Reduce(IMoniker* iface,
728                                             IBindCtx* pbc,
729                                             DWORD dwReduceHowFar,
730                                             IMoniker** ppmkToLeft,
731                                             IMoniker** ppmkReduced)
732 {
733     URLMonikerImpl *This = (URLMonikerImpl *)iface;
734     
735     TRACE("(%p,%p,%ld,%p,%p)\n",This,pbc,dwReduceHowFar,ppmkToLeft,ppmkReduced);
736
737     if(!ppmkReduced)
738         return E_INVALIDARG;
739
740     URLMonikerImpl_AddRef(iface);
741     *ppmkReduced = iface;
742     return MK_S_REDUCED_TO_SELF;
743 }
744
745 /******************************************************************************
746  *        URLMoniker_ComposeWith
747  ******************************************************************************/
748 static HRESULT WINAPI URLMonikerImpl_ComposeWith(IMoniker* iface,
749                                                  IMoniker* pmkRight,
750                                                  BOOL fOnlyIfNotGeneric,
751                                                  IMoniker** ppmkComposite)
752 {
753     URLMonikerImpl *This = (URLMonikerImpl *)iface;
754     FIXME("(%p)->(%p,%d,%p): stub\n",This,pmkRight,fOnlyIfNotGeneric,ppmkComposite);
755
756     return E_NOTIMPL;
757 }
758
759 /******************************************************************************
760  *        URLMoniker_Enum
761  ******************************************************************************/
762 static HRESULT WINAPI URLMonikerImpl_Enum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker)
763 {
764     URLMonikerImpl *This = (URLMonikerImpl *)iface;
765     TRACE("(%p,%d,%p)\n",This,fForward,ppenumMoniker);
766
767     if(!ppenumMoniker)
768         return E_INVALIDARG;
769
770     /* Does not support sub-monikers */
771     *ppenumMoniker = NULL;
772     return S_OK;
773 }
774
775 /******************************************************************************
776  *        URLMoniker_IsEqual
777  ******************************************************************************/
778 static HRESULT WINAPI URLMonikerImpl_IsEqual(IMoniker* iface,IMoniker* pmkOtherMoniker)
779 {
780     URLMonikerImpl *This = (URLMonikerImpl *)iface;
781     CLSID clsid;
782     LPOLESTR urlPath;
783     IBindCtx* bind;
784     HRESULT res;
785
786     TRACE("(%p,%p)\n",This,pmkOtherMoniker);
787
788     if(pmkOtherMoniker==NULL)
789         return E_INVALIDARG;
790
791     IMoniker_GetClassID(pmkOtherMoniker,&clsid);
792
793     if(!IsEqualCLSID(&clsid,&CLSID_StdURLMoniker))
794         return S_FALSE;
795
796     res = CreateBindCtx(0,&bind);
797     if(FAILED(res))
798         return res;
799
800     res = S_FALSE;
801     if(SUCCEEDED(IMoniker_GetDisplayName(pmkOtherMoniker,bind,NULL,&urlPath))) {
802         int result = lstrcmpiW(urlPath, This->URLName);
803         CoTaskMemFree(urlPath);
804         if(result == 0)
805             res = S_OK;
806     }
807     IUnknown_Release(bind);
808     return res;
809 }
810
811
812 /******************************************************************************
813  *        URLMoniker_Hash
814  ******************************************************************************/
815 static HRESULT WINAPI URLMonikerImpl_Hash(IMoniker* iface,DWORD* pdwHash)
816 {
817     URLMonikerImpl *This = (URLMonikerImpl *)iface;
818     
819     int  h = 0,i,skip,len;
820     int  off = 0;
821     LPOLESTR val;
822
823     TRACE("(%p,%p)\n",This,pdwHash);
824
825     if(!pdwHash)
826         return E_INVALIDARG;
827
828     val = This->URLName;
829     len = lstrlenW(val);
830
831     if(len < 16) {
832         for(i = len ; i > 0; i--) {
833             h = (h * 37) + val[off++];
834         }
835     }
836     else {
837         /* only sample some characters */
838         skip = len / 8;
839         for(i = len; i > 0; i -= skip, off += skip) {
840             h = (h * 39) + val[off];
841         }
842     }
843     *pdwHash = h;
844     return S_OK;
845 }
846
847 /******************************************************************************
848  *        URLMoniker_IsRunning
849  ******************************************************************************/
850 static HRESULT WINAPI URLMonikerImpl_IsRunning(IMoniker* iface,
851                                                IBindCtx* pbc,
852                                                IMoniker* pmkToLeft,
853                                                IMoniker* pmkNewlyRunning)
854 {
855     URLMonikerImpl *This = (URLMonikerImpl *)iface;
856     FIXME("(%p)->(%p,%p,%p): stub\n",This,pbc,pmkToLeft,pmkNewlyRunning);
857
858     return E_NOTIMPL;
859 }
860
861 /******************************************************************************
862  *        URLMoniker_GetTimeOfLastChange
863  ******************************************************************************/
864 static HRESULT WINAPI URLMonikerImpl_GetTimeOfLastChange(IMoniker* iface,
865                                                          IBindCtx* pbc,
866                                                          IMoniker* pmkToLeft,
867                                                          FILETIME* pFileTime)
868 {
869     URLMonikerImpl *This = (URLMonikerImpl *)iface;
870     FIXME("(%p)->(%p,%p,%p): stub\n",This,pbc,pmkToLeft,pFileTime);
871
872     return E_NOTIMPL;
873 }
874
875 /******************************************************************************
876  *        URLMoniker_Inverse
877  ******************************************************************************/
878 static HRESULT WINAPI URLMonikerImpl_Inverse(IMoniker* iface,IMoniker** ppmk)
879 {
880     URLMonikerImpl *This = (URLMonikerImpl *)iface;
881     TRACE("(%p,%p)\n",This,ppmk);
882
883     return MK_E_NOINVERSE;
884 }
885
886 /******************************************************************************
887  *        URLMoniker_CommonPrefixWith
888  ******************************************************************************/
889 static HRESULT WINAPI URLMonikerImpl_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix)
890 {
891     URLMonikerImpl *This = (URLMonikerImpl *)iface;
892     FIXME("(%p)->(%p,%p): stub\n",This,pmkOther,ppmkPrefix);
893
894     return E_NOTIMPL;
895 }
896
897 /******************************************************************************
898  *        URLMoniker_RelativePathTo
899  ******************************************************************************/
900 static HRESULT WINAPI URLMonikerImpl_RelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath)
901 {
902     URLMonikerImpl *This = (URLMonikerImpl *)iface;
903     FIXME("(%p)->(%p,%p): stub\n",This,pmOther,ppmkRelPath);
904
905     return E_NOTIMPL;
906 }
907
908 /******************************************************************************
909  *        URLMoniker_GetDisplayName
910  ******************************************************************************/
911 static HRESULT WINAPI URLMonikerImpl_GetDisplayName(IMoniker* iface,
912                                                     IBindCtx* pbc,
913                                                     IMoniker* pmkToLeft,
914                                                     LPOLESTR *ppszDisplayName)
915 {
916     URLMonikerImpl *This = (URLMonikerImpl *)iface;
917     
918     int len;
919     
920     TRACE("(%p,%p,%p,%p)\n",This,pbc,pmkToLeft,ppszDisplayName);
921     
922     if(!ppszDisplayName)
923         return E_INVALIDARG;
924     
925     /* FIXME: If this is a partial URL, try and get a URL moniker from SZ_URLCONTEXT in the bind context,
926         then look at pmkToLeft to try and complete the URL
927     */
928     len = lstrlenW(This->URLName)+1;
929     *ppszDisplayName = CoTaskMemAlloc(len*sizeof(WCHAR));
930     if(!*ppszDisplayName)
931         return E_OUTOFMEMORY;
932     lstrcpyW(*ppszDisplayName, This->URLName);
933     return S_OK;
934 }
935
936 /******************************************************************************
937  *        URLMoniker_ParseDisplayName
938  ******************************************************************************/
939 static HRESULT WINAPI URLMonikerImpl_ParseDisplayName(IMoniker* iface,
940                                                       IBindCtx* pbc,
941                                                       IMoniker* pmkToLeft,
942                                                       LPOLESTR pszDisplayName,
943                                                       ULONG* pchEaten,
944                                                       IMoniker** ppmkOut)
945 {
946     URLMonikerImpl *This = (URLMonikerImpl *)iface;
947     FIXME("(%p)->(%p,%p,%p,%p,%p): stub\n",This,pbc,pmkToLeft,pszDisplayName,pchEaten,ppmkOut);
948
949     return E_NOTIMPL;
950 }
951
952 /******************************************************************************
953  *        URLMoniker_IsSystemMoniker
954  ******************************************************************************/
955 static HRESULT WINAPI URLMonikerImpl_IsSystemMoniker(IMoniker* iface,DWORD* pwdMksys)
956 {
957     URLMonikerImpl *This = (URLMonikerImpl *)iface;
958     TRACE("(%p,%p)\n",This,pwdMksys);
959
960     if(!pwdMksys)
961         return E_INVALIDARG;
962
963     *pwdMksys = MKSYS_URLMONIKER;
964     return S_OK;
965 }
966
967 static HRESULT WINAPI URLMonikerImpl_IBinding_QueryInterface(IBinding* iface,REFIID riid,void** ppvObject)
968 {
969     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
970
971     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppvObject);
972
973     /* Perform a sanity check on the parameters.*/
974     if ( (This==0) || (ppvObject==0) )
975         return E_INVALIDARG;
976
977     /* Initialize the return parameter */
978     *ppvObject = 0;
979
980     /* Compare the riid with the interface IDs implemented by this object.*/
981     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IBinding, riid))
982         *ppvObject = iface;
983
984     /* Check that we obtained an interface.*/
985     if ((*ppvObject)==0)
986         return E_NOINTERFACE;
987
988     /* Query Interface always increases the reference count by one when it is successful */
989     IBinding_AddRef(iface);
990
991     return S_OK;
992
993 }
994
995 static ULONG WINAPI URLMonikerImpl_IBinding_AddRef(IBinding* iface)
996 {
997     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
998     TRACE("(%p)\n",This);
999
1000     return URLMonikerImpl_AddRef((IMoniker*)This);
1001 }
1002
1003 static ULONG WINAPI URLMonikerImpl_IBinding_Release(IBinding* iface)
1004 {
1005     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1006     TRACE("(%p)\n",This);
1007
1008     return URLMonikerImpl_Release((IMoniker*)This);
1009 }
1010
1011 static HRESULT WINAPI URLMonikerImpl_IBinding_Abort(IBinding* iface)
1012 {
1013     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1014     FIXME("(%p): stub\n", This);
1015
1016     return E_NOTIMPL;
1017 }
1018
1019 static HRESULT WINAPI URLMonikerImpl_IBinding_GetBindResult(IBinding* iface, CLSID* pclsidProtocol, DWORD* pdwResult, LPOLESTR* pszResult, DWORD* pdwReserved)
1020 {
1021     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1022     FIXME("(%p)->(%p, %p, %p, %p): stub\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved);
1023
1024     return E_NOTIMPL;
1025 }
1026
1027 static HRESULT WINAPI URLMonikerImpl_IBinding_GetPriority(IBinding* iface, LONG* pnPriority)
1028 {
1029     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1030     FIXME("(%p)->(%p): stub\n", This, pnPriority);
1031
1032     return E_NOTIMPL;
1033 }
1034
1035 static HRESULT WINAPI URLMonikerImpl_IBinding_Resume(IBinding* iface)
1036 {
1037     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1038     FIXME("(%p): stub\n", This);
1039
1040     return E_NOTIMPL;
1041 }
1042
1043 static HRESULT WINAPI URLMonikerImpl_IBinding_SetPriority(IBinding* iface, LONG nPriority)
1044 {
1045     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1046     FIXME("(%p)->(%ld): stub\n", This, nPriority);
1047
1048     return E_NOTIMPL;
1049 }
1050
1051 static HRESULT WINAPI URLMonikerImpl_IBinding_Suspend(IBinding* iface)
1052 {
1053     ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface);
1054     FIXME("(%p): stub\n", This);
1055
1056     return E_NOTIMPL;
1057 }
1058
1059 /********************************************************************************/
1060 /* Virtual function table for the URLMonikerImpl class which  include IPersist,*/
1061 /* IPersistStream and IMoniker functions.                                       */
1062 static IMonikerVtbl VT_URLMonikerImpl =
1063 {
1064     URLMonikerImpl_QueryInterface,
1065     URLMonikerImpl_AddRef,
1066     URLMonikerImpl_Release,
1067     URLMonikerImpl_GetClassID,
1068     URLMonikerImpl_IsDirty,
1069     URLMonikerImpl_Load,
1070     URLMonikerImpl_Save,
1071     URLMonikerImpl_GetSizeMax,
1072     URLMonikerImpl_BindToObject,
1073     URLMonikerImpl_BindToStorage,
1074     URLMonikerImpl_Reduce,
1075     URLMonikerImpl_ComposeWith,
1076     URLMonikerImpl_Enum,
1077     URLMonikerImpl_IsEqual,
1078     URLMonikerImpl_Hash,
1079     URLMonikerImpl_IsRunning,
1080     URLMonikerImpl_GetTimeOfLastChange,
1081     URLMonikerImpl_Inverse,
1082     URLMonikerImpl_CommonPrefixWith,
1083     URLMonikerImpl_RelativePathTo,
1084     URLMonikerImpl_GetDisplayName,
1085     URLMonikerImpl_ParseDisplayName,
1086     URLMonikerImpl_IsSystemMoniker
1087 };
1088
1089 static IBindingVtbl VTBinding_URLMonikerImpl =
1090 {
1091     URLMonikerImpl_IBinding_QueryInterface,
1092     URLMonikerImpl_IBinding_AddRef,
1093     URLMonikerImpl_IBinding_Release,
1094     URLMonikerImpl_IBinding_Abort,
1095     URLMonikerImpl_IBinding_Suspend,
1096     URLMonikerImpl_IBinding_Resume,
1097     URLMonikerImpl_IBinding_SetPriority,
1098     URLMonikerImpl_IBinding_GetPriority,
1099     URLMonikerImpl_IBinding_GetBindResult
1100 };
1101
1102 /******************************************************************************
1103  *         URLMoniker_Construct (local function)
1104  *******************************************************************************/
1105 static HRESULT URLMonikerImpl_Construct(URLMonikerImpl* This, LPCOLESTR lpszLeftURLName, LPCOLESTR lpszURLName)
1106 {
1107     HRESULT hres;
1108     DWORD sizeStr;
1109
1110     TRACE("(%p,%s,%s)\n",This,debugstr_w(lpszLeftURLName),debugstr_w(lpszURLName));
1111     memset(This, 0, sizeof(*This));
1112
1113     /* Initialize the virtual function table. */
1114     This->lpvtbl1      = &VT_URLMonikerImpl;
1115     This->lpvtbl2      = &VTBinding_URLMonikerImpl;
1116     This->ref          = 0;
1117
1118     if(lpszLeftURLName) {
1119         hres = UrlCombineW(lpszLeftURLName, lpszURLName, NULL, &sizeStr, 0);
1120         if(FAILED(hres)) {
1121             return hres;
1122         }
1123         sizeStr++;
1124     }
1125     else
1126         sizeStr = lstrlenW(lpszURLName)+1;
1127
1128     This->URLName=HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*(sizeStr));
1129
1130     if (This->URLName==NULL)
1131         return E_OUTOFMEMORY;
1132
1133     if(lpszLeftURLName) {
1134         hres = UrlCombineW(lpszLeftURLName, lpszURLName, This->URLName, &sizeStr, 0);
1135         if(FAILED(hres)) {
1136             HeapFree(GetProcessHeap(), 0, This->URLName);
1137             return hres;
1138         }
1139     }
1140     else
1141         strcpyW(This->URLName,lpszURLName);
1142
1143     return S_OK;
1144 }
1145
1146 /***********************************************************************
1147  *           CreateAsyncBindCtx (URLMON.@)
1148  */
1149 HRESULT WINAPI CreateAsyncBindCtx(DWORD reserved, IBindStatusCallback *callback,
1150     IEnumFORMATETC *format, IBindCtx **pbind)
1151 {
1152     HRESULT hres;
1153     BIND_OPTS bindopts;
1154     IBindCtx *bctx;
1155
1156     TRACE("(%08lx %p %p %p)\n", reserved, callback, format, pbind);
1157
1158     if(!callback)
1159         return E_INVALIDARG;
1160     if(format)
1161         FIXME("format is not supported yet\n");
1162
1163     hres = CreateBindCtx(0, &bctx);
1164     if(FAILED(hres))
1165         return hres;
1166
1167     bindopts.cbStruct = sizeof(BIND_OPTS);
1168     bindopts.grfFlags = BIND_MAYBOTHERUSER;
1169     bindopts.grfMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
1170     bindopts.dwTickCountDeadline = 0;
1171     IBindCtx_SetBindOptions(bctx, &bindopts);
1172
1173     hres = IBindCtx_RegisterObjectParam(bctx, (LPOLESTR)BSCBHolder, (IUnknown*)callback);
1174     if(FAILED(hres)) {
1175         IBindCtx_Release(bctx);
1176         return hres;
1177     }
1178
1179     *pbind = bctx;
1180
1181     return S_OK;
1182 }
1183 /***********************************************************************
1184  *           CreateAsyncBindCtxEx (URLMON.@)
1185  *
1186  * Create an asynchronous bind context.
1187  *
1188  * FIXME
1189  *   Not implemented.
1190  */ 
1191 HRESULT WINAPI CreateAsyncBindCtxEx(IBindCtx *ibind, DWORD options,
1192     IBindStatusCallback *callback, IEnumFORMATETC *format, IBindCtx** pbind,
1193     DWORD reserved)
1194 {
1195      FIXME("stub, returns failure\n");
1196      return E_INVALIDARG;
1197 }
1198
1199
1200 /***********************************************************************
1201  *           CreateURLMoniker (URLMON.@)
1202  *
1203  * Create a url moniker.
1204  *
1205  * PARAMS
1206  *    pmkContext [I] Context
1207  *    szURL      [I] Url to create the moniker for
1208  *    ppmk       [O] Destination for created moniker.
1209  *
1210  * RETURNS
1211  *    Success: S_OK. ppmk contains the created IMoniker object.
1212  *    Failure: MK_E_SYNTAX if szURL is not a valid url, or
1213  *             E_OUTOFMEMORY if memory allocation fails.
1214  */
1215 HRESULT WINAPI CreateURLMoniker(IMoniker *pmkContext, LPCWSTR szURL, IMoniker **ppmk)
1216 {
1217     URLMonikerImpl *obj;
1218     HRESULT hres;
1219     IID iid = IID_IMoniker;
1220     LPOLESTR lefturl = NULL;
1221
1222     TRACE("(%p, %s, %p)\n", pmkContext, debugstr_w(szURL), ppmk);
1223
1224     if(!(obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj))))
1225         return E_OUTOFMEMORY;
1226
1227     if(pmkContext) {
1228         CLSID clsid;
1229         IBindCtx* bind;
1230         IMoniker_GetClassID(pmkContext, &clsid);
1231         if(IsEqualCLSID(&clsid, &CLSID_StdURLMoniker) && SUCCEEDED(CreateBindCtx(0, &bind))) {
1232             URLMonikerImpl_GetDisplayName(pmkContext, bind, NULL, &lefturl);
1233             IBindCtx_Release(bind);
1234         }
1235     }
1236         
1237     hres = URLMonikerImpl_Construct(obj, lefturl, szURL);
1238     CoTaskMemFree(lefturl);
1239     if(SUCCEEDED(hres))
1240         hres = URLMonikerImpl_QueryInterface((IMoniker*)obj, &iid, (void**)ppmk);
1241     else
1242         HeapFree(GetProcessHeap(), 0, obj);
1243     return hres;
1244 }
1245
1246
1247 /***********************************************************************
1248  *           CoInternetGetSession (URLMON.@)
1249  *
1250  * Create a new internet session and return an IInternetSession interface
1251  * representing it.
1252  *
1253  * PARAMS
1254  *    dwSessionMode      [I] Mode for the internet session
1255  *    ppIInternetSession [O] Destination for creates IInternetSession object
1256  *    dwReserved         [I] Reserved, must be 0.
1257  *
1258  * RETURNS
1259  *    Success: S_OK. ppIInternetSession contains the IInternetSession interface.
1260  *    Failure: E_INVALIDARG, if any argument is invalid, or
1261  *             E_OUTOFMEMORY if memory allocation fails.
1262  */
1263 HRESULT WINAPI CoInternetGetSession(DWORD dwSessionMode, IInternetSession **ppIInternetSession, DWORD dwReserved)
1264 {
1265     FIXME("(%ld, %p, %ld): stub\n", dwSessionMode, ppIInternetSession, dwReserved);
1266
1267     if(dwSessionMode) {
1268       ERR("dwSessionMode: %ld, must be zero\n", dwSessionMode);
1269     }
1270
1271     if(dwReserved) {
1272       ERR("dwReserved: %ld, must be zero\n", dwReserved);
1273     }
1274
1275     *ppIInternetSession=NULL;
1276     return E_OUTOFMEMORY;
1277 }
1278
1279 /***********************************************************************
1280  *           CoInternetQueryInfo (URLMON.@)
1281  *
1282  * Retrieves information relevant to a specified URL
1283  *
1284  * RETURNS
1285  *    S_OK                      success
1286  *    S_FALSE                   buffer too small
1287  *    INET_E_QUERYOPTIONUNKNOWN invalid option
1288  *
1289  */
1290 HRESULT WINAPI CoInternetQueryInfo(LPCWSTR pwzUrl, QUERYOPTION QueryOption,
1291   DWORD dwQueryFlags, LPVOID pvBuffer, DWORD cbBuffer, DWORD * pcbBuffer,
1292   DWORD dwReserved)
1293 {
1294   FIXME("(%s, %x, %lx, %p, %lx, %p, %lx): stub\n", debugstr_w(pwzUrl),
1295     QueryOption, dwQueryFlags, pvBuffer, cbBuffer, pcbBuffer, dwReserved);
1296   return S_OK;
1297 }
1298
1299 static BOOL URLMON_IsBinary(LPVOID pBuffer, DWORD cbSize)
1300 {
1301     unsigned int i, binarycount = 0;
1302     unsigned char *buff = pBuffer;
1303     for(i=0; i<cbSize; i++) {
1304         if(buff[i] < 32)
1305             binarycount++;
1306     }
1307     return binarycount > (cbSize-binarycount);
1308 }
1309
1310 /***********************************************************************
1311  *           FindMimeFromData (URLMON.@)
1312  *
1313  * Determines the Multipurpose Internet Mail Extensions (MIME) type from the data provided.
1314  *
1315  * NOTE
1316  *  See http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp
1317  */
1318 HRESULT WINAPI FindMimeFromData(LPBC pBC, LPCWSTR pwzUrl, LPVOID pBuffer,
1319    DWORD cbSize, LPCWSTR pwzMimeProposed, DWORD dwMimeFlags,
1320    LPWSTR* ppwzMimeOut, DWORD dwReserved)
1321 {
1322     static const WCHAR szBinaryMime[] = {'a','p','p','l','i','c','a','t','i','o','n','/','o','c','t','e','t','-','s','t','r','e','a','m','\0'};
1323     static const WCHAR szTextMime[] = {'t','e','x','t','/','p','l','a','i','n','\0'};
1324     static const WCHAR szContentType[] = {'C','o','n','t','e','n','t',' ','T','y','p','e','\0'};
1325     WCHAR szTmpMime[256];
1326     LPCWSTR mimeType = NULL;
1327     HKEY hKey = NULL;
1328
1329     TRACE("(%p,%s,%p,%ld,%s,0x%lx,%p,0x%lx)\n", pBC, debugstr_w(pwzUrl), pBuffer, cbSize,
1330           debugstr_w(pwzMimeProposed), dwMimeFlags, ppwzMimeOut, dwReserved);
1331
1332     if((!pwzUrl && (!pBuffer || cbSize <= 0)) || !ppwzMimeOut)
1333         return E_INVALIDARG;
1334
1335     if(pwzMimeProposed)
1336         mimeType = pwzMimeProposed;
1337     else {
1338         /* Try and find the mime type in the registry */
1339         if(pwzUrl) {
1340             LPWSTR ext = strrchrW(pwzUrl, '.');
1341             if(ext) {
1342                 DWORD dwSize;
1343                 if(!RegOpenKeyExW(HKEY_CLASSES_ROOT, ext, 0, 0, &hKey)) {
1344                     if(!RegQueryValueExW(hKey, szContentType, NULL, NULL, (LPBYTE)szTmpMime, &dwSize)) {
1345                         mimeType = szTmpMime;
1346                     }
1347                     RegCloseKey(hKey);
1348                 }
1349             }
1350         }
1351     }
1352     if(!mimeType && pBuffer && cbSize > 0)
1353         mimeType = URLMON_IsBinary(pBuffer, cbSize)?szBinaryMime:szTextMime;
1354
1355     TRACE("Using %s\n", debugstr_w(mimeType));
1356     *ppwzMimeOut = CoTaskMemAlloc((lstrlenW(mimeType)+1)*sizeof(WCHAR));
1357     if(!*ppwzMimeOut) return E_OUTOFMEMORY;
1358     lstrcpyW(*ppwzMimeOut, mimeType);
1359     return S_OK;
1360 }
1361
1362 /***********************************************************************
1363  *           IsAsyncMoniker (URLMON.@)
1364  */
1365 HRESULT WINAPI IsAsyncMoniker(IMoniker *pmk)
1366 {
1367     IUnknown *am;
1368     
1369     TRACE("(%p)\n", pmk);
1370     if(!pmk)
1371         return E_INVALIDARG;
1372     if(SUCCEEDED(IMoniker_QueryInterface(pmk, &IID_IAsyncMoniker, (void**)&am))) {
1373         IUnknown_Release(am);
1374         return S_OK;
1375     }
1376     return S_FALSE;
1377 }
1378
1379 /***********************************************************************
1380  *           RegisterBindStatusCallback (URLMON.@)
1381  *
1382  * Register a bind status callback.
1383  *
1384  * PARAMS
1385  *  pbc           [I] Binding context
1386  *  pbsc          [I] Callback to register
1387  *  ppbscPrevious [O] Destination for previous callback
1388  *  dwReserved    [I] Reserved, must be 0.
1389  *
1390  * RETURNS
1391  *    Success: S_OK.
1392  *    Failure: E_INVALIDARG, if any argument is invalid, or
1393  *             E_OUTOFMEMORY if memory allocation fails.
1394  */
1395 HRESULT WINAPI RegisterBindStatusCallback(
1396     IBindCtx *pbc,
1397     IBindStatusCallback *pbsc,
1398     IBindStatusCallback **ppbscPrevious,
1399     DWORD dwReserved)
1400 {
1401     IBindStatusCallback *prev;
1402
1403     TRACE("(%p,%p,%p,%lu)\n", pbc, pbsc, ppbscPrevious, dwReserved);
1404
1405     if (pbc == NULL || pbsc == NULL)
1406         return E_INVALIDARG;
1407
1408     if (SUCCEEDED(IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown **)&prev)))
1409     {
1410         IBindCtx_RevokeObjectParam(pbc, (LPOLESTR)BSCBHolder);
1411         if (ppbscPrevious)
1412             *ppbscPrevious = prev;
1413         else
1414             IBindStatusCallback_Release(prev);
1415     }
1416
1417         return IBindCtx_RegisterObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown *)pbsc);
1418 }
1419
1420 /***********************************************************************
1421  *           RevokeBindStatusCallback (URLMON.@)
1422  *
1423  * Unregister a bind status callback.
1424  *
1425  *  pbc           [I] Binding context
1426  *  pbsc          [I] Callback to unregister
1427  *
1428  * RETURNS
1429  *    Success: S_OK.
1430  *    Failure: E_INVALIDARG, if any argument is invalid, or
1431  *             E_FAIL if pbsc wasn't registered with pbc.
1432  */
1433 HRESULT WINAPI RevokeBindStatusCallback(
1434     IBindCtx *pbc,
1435     IBindStatusCallback *pbsc)
1436 {
1437     IBindStatusCallback *callback;
1438     HRESULT hr = E_FAIL;
1439
1440         TRACE("(%p,%p)\n", pbc, pbsc);
1441
1442     if (pbc == NULL || pbsc == NULL)
1443         return E_INVALIDARG;
1444
1445     if (SUCCEEDED(IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown **)&callback)))
1446     {
1447         if (callback == pbsc)
1448         {
1449             IBindCtx_RevokeObjectParam(pbc, (LPOLESTR)BSCBHolder);
1450             hr = S_OK;
1451         }
1452         IBindStatusCallback_Release(pbsc);
1453     }
1454
1455     return hr;
1456 }
1457
1458 /***********************************************************************
1459  *           ReleaseBindInfo (URLMON.@)
1460  *
1461  * Release the resources used by the specified BINDINFO structure.
1462  *
1463  * PARAMS
1464  *  pbindinfo [I] BINDINFO to release.
1465  *
1466  * RETURNS
1467  *  Nothing.
1468  */
1469 void WINAPI ReleaseBindInfo(BINDINFO* pbindinfo)
1470 {
1471     FIXME("(%p)stub!\n", pbindinfo);
1472 }
1473
1474 /***********************************************************************
1475  *           URLDownloadToFileA (URLMON.@)
1476  *
1477  * Downloads URL szURL to rile szFileName and call lpfnCB callback to
1478  * report progress.
1479  *
1480  * PARAMS
1481  *  pCaller    [I] controlling IUnknown interface.
1482  *  szURL      [I] URL of the file to download
1483  *  szFileName [I] file name to store the content of the URL
1484  *  dwReserved [I] reserved - set to 0
1485  *  lpfnCB     [I] callback for progress report
1486  *
1487  * RETURNS
1488  *  S_OK on success
1489  *  E_OUTOFMEMORY when going out of memory
1490  */
1491 HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller,
1492                                   LPCSTR szURL,
1493                                   LPCSTR szFileName,
1494                                   DWORD dwReserved,
1495                                   LPBINDSTATUSCALLBACK lpfnCB)
1496 {
1497     UNICODE_STRING szURL_w, szFileName_w;
1498
1499     if ((szURL == NULL) || (szFileName == NULL)) {
1500         FIXME("(%p,%s,%s,%08lx,%p) cannot accept NULL strings !\n", pCaller, debugstr_a(szURL), debugstr_a(szFileName), dwReserved, lpfnCB);
1501         return E_INVALIDARG; /* The error code is not specified in this case... */
1502     }
1503     
1504     if (RtlCreateUnicodeStringFromAsciiz(&szURL_w, szURL)) {
1505         if (RtlCreateUnicodeStringFromAsciiz(&szFileName_w, szFileName)) {
1506             HRESULT ret = URLDownloadToFileW(pCaller, szURL_w.Buffer, szFileName_w.Buffer, dwReserved, lpfnCB);
1507
1508             RtlFreeUnicodeString(&szURL_w);
1509             RtlFreeUnicodeString(&szFileName_w);
1510             
1511             return ret;
1512         } else {
1513             RtlFreeUnicodeString(&szURL_w);
1514         }
1515     }
1516     
1517     FIXME("(%p,%s,%s,%08lx,%p) could not allocate W strings !\n", pCaller, szURL, szFileName, dwReserved, lpfnCB);
1518     return E_OUTOFMEMORY;
1519 }
1520
1521 /***********************************************************************
1522  *           URLDownloadToFileW (URLMON.@)
1523  *
1524  * Downloads URL szURL to rile szFileName and call lpfnCB callback to
1525  * report progress.
1526  *
1527  * PARAMS
1528  *  pCaller    [I] controlling IUnknown interface.
1529  *  szURL      [I] URL of the file to download
1530  *  szFileName [I] file name to store the content of the URL
1531  *  dwReserved [I] reserved - set to 0
1532  *  lpfnCB     [I] callback for progress report
1533  *
1534  * RETURNS
1535  *  S_OK on success
1536  *  E_OUTOFMEMORY when going out of memory
1537  */
1538 HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller,
1539                                   LPCWSTR szURL,
1540                                   LPCWSTR szFileName,
1541                                   DWORD dwReserved,
1542                                   LPBINDSTATUSCALLBACK lpfnCB)
1543 {
1544     HINTERNET hinternet, hcon, hreq;
1545     BOOL r;
1546     CHAR buffer[0x1000];
1547     DWORD sz, total, written;
1548     DWORD total_size = 0xFFFFFFFF, arg_size = sizeof(total_size);
1549     URL_COMPONENTSW url;
1550     WCHAR host[0x80], path[0x100];
1551     HANDLE hfile;
1552     static const WCHAR wszAppName[]={'u','r','l','m','o','n','.','d','l','l',0};
1553
1554     /* Note: all error codes would need to be checked agains real Windows behaviour... */
1555     TRACE("(%p,%s,%s,%08lx,%p) stub!\n", pCaller, debugstr_w(szURL), debugstr_w(szFileName), dwReserved, lpfnCB);
1556
1557     if ((szURL == NULL) || (szFileName == NULL)) {
1558         FIXME(" cannot accept NULL strings !\n");
1559         return E_INVALIDARG;
1560     }
1561
1562     /* Would be better to use the application name here rather than 'urlmon' :-/ */
1563     hinternet = InternetOpenW(wszAppName, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
1564     if (hinternet == NULL) {
1565         return E_OUTOFMEMORY;
1566     }                                                                                                                             
1567
1568     memset(&url, 0, sizeof(url));
1569     url.dwStructSize = sizeof(url);
1570     url.lpszHostName = host;
1571     url.dwHostNameLength = sizeof(host);
1572     url.lpszUrlPath = path;
1573     url.dwUrlPathLength = sizeof(path);
1574
1575     if (!InternetCrackUrlW(szURL, 0, 0, &url)) {
1576         InternetCloseHandle(hinternet);
1577         return E_OUTOFMEMORY;
1578     }
1579
1580     if (lpfnCB) {
1581         if (IBindStatusCallback_OnProgress(lpfnCB, 0, 0, BINDSTATUS_CONNECTING, url.lpszHostName) == E_ABORT) {
1582             InternetCloseHandle(hinternet);
1583             return S_OK;
1584         }
1585     }
1586     
1587     hcon = InternetConnectW(hinternet, url.lpszHostName, url.nPort,
1588                             url.lpszUserName, url.lpszPassword,
1589                             INTERNET_SERVICE_HTTP, 0, 0);
1590     if (!hcon) {
1591         InternetCloseHandle(hinternet);
1592         return E_OUTOFMEMORY;
1593     }
1594     
1595     hreq = HttpOpenRequestW(hcon, NULL, url.lpszUrlPath, NULL, NULL, NULL, 0, 0);
1596     if (!hreq) {
1597         InternetCloseHandle(hinternet);
1598         InternetCloseHandle(hcon);
1599         return E_OUTOFMEMORY;
1600     }                                                                                                                             
1601
1602     if (!HttpSendRequestW(hreq, NULL, 0, NULL, 0)) {
1603         InternetCloseHandle(hinternet);
1604         InternetCloseHandle(hcon);
1605         InternetCloseHandle(hreq);
1606         return E_OUTOFMEMORY;
1607     }
1608     
1609     if (HttpQueryInfoW(hreq, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
1610                        &total_size, &arg_size, NULL)) {
1611         TRACE(" total size : %ld\n", total_size);
1612     }
1613     
1614     hfile = CreateFileW(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1615                         FILE_ATTRIBUTE_NORMAL, NULL );
1616     if (hfile == INVALID_HANDLE_VALUE) {
1617         return E_ACCESSDENIED;
1618     }
1619     
1620     if (lpfnCB) {
1621         if (IBindStatusCallback_OnProgress(lpfnCB, 0, total_size != 0xFFFFFFFF ? total_size : 0,
1622                                            BINDSTATUS_BEGINDOWNLOADDATA, szURL) == E_ABORT) {
1623             InternetCloseHandle(hreq);
1624             InternetCloseHandle(hcon);
1625             InternetCloseHandle(hinternet);
1626             CloseHandle(hfile);
1627             return S_OK;
1628         }
1629     }
1630     
1631     total = 0;
1632     while (1) {
1633         r = InternetReadFile(hreq, buffer, sizeof(buffer), &sz);
1634         if (!r) {
1635             InternetCloseHandle(hreq);
1636             InternetCloseHandle(hcon);
1637             InternetCloseHandle(hinternet);
1638             
1639             CloseHandle(hfile);
1640             return E_OUTOFMEMORY;           
1641         }
1642         if (!sz)
1643             break;
1644         
1645         total += sz;
1646
1647         if (lpfnCB) {
1648             if (IBindStatusCallback_OnProgress(lpfnCB, total, total_size != 0xFFFFFFFF ? total_size : 0,
1649                                                BINDSTATUS_DOWNLOADINGDATA, szURL) == E_ABORT) {
1650                 InternetCloseHandle(hreq);
1651                 InternetCloseHandle(hcon);
1652                 InternetCloseHandle(hinternet);
1653                 CloseHandle(hfile);
1654                 return S_OK;
1655             }
1656         }
1657         
1658         if (!WriteFile(hfile, buffer, sz, &written, NULL)) {
1659             InternetCloseHandle(hreq);
1660             InternetCloseHandle(hcon);
1661             InternetCloseHandle(hinternet);
1662             
1663             CloseHandle(hfile);
1664             return E_OUTOFMEMORY;
1665         }
1666     }
1667
1668     if (lpfnCB) {
1669         if (IBindStatusCallback_OnProgress(lpfnCB, total, total_size != 0xFFFFFFFF ? total_size : 0,
1670                                            BINDSTATUS_ENDDOWNLOADDATA, szURL) == E_ABORT) {
1671             InternetCloseHandle(hreq);
1672             InternetCloseHandle(hcon);
1673             InternetCloseHandle(hinternet);
1674             CloseHandle(hfile);
1675             return S_OK;
1676         }
1677     }
1678     
1679     InternetCloseHandle(hreq);
1680     InternetCloseHandle(hcon);
1681     InternetCloseHandle(hinternet);
1682     
1683     CloseHandle(hfile);
1684
1685     return S_OK;
1686 }
1687
1688 /***********************************************************************
1689  *           HlinkSimpleNavigateToString (URLMON.@)
1690  */
1691 HRESULT WINAPI HlinkSimpleNavigateToString( LPCWSTR szTarget,
1692     LPCWSTR szLocation, LPCWSTR szTargetFrameName, IUnknown *pUnk,
1693     IBindCtx *pbc, IBindStatusCallback *pbsc, DWORD grfHLNF, DWORD dwReserved)
1694 {
1695     FIXME("%s\n", debugstr_w( szTarget ) );
1696     return E_NOTIMPL;
1697 }
1698
1699 /***********************************************************************
1700  *           HlinkNavigateString (URLMON.@)
1701  */
1702 HRESULT WINAPI HlinkNavigateString( IUnknown *pUnk, LPCWSTR szTarget )
1703 {
1704     TRACE("%p %s\n", pUnk, debugstr_w( szTarget ) );
1705     return HlinkSimpleNavigateToString( 
1706                szTarget, NULL, NULL, pUnk, NULL, NULL, 0, 0 );
1707 }