camnet.cpp

Go to the documentation of this file.
00001 // $Id: camnet.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 #include "camtypes.h"
00099 #include <fstream.h>
00100 #include <io.h>
00101 #include <stdlib.h>
00102 #include <direct.h>
00103 #include <stdio.h>
00104 #include <errno.h>
00105 #include <string.h>
00106 #include <process.h>
00107 #include "sglcart.h"
00108 #include "pathnmex.h"
00109 #include "camnet.h"
00110 #include "sgliboil.h"
00111 //#include "webster.h"
00112 //#include "thumb.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "registry.h"
00114 //#include "resource.h"
00115 //#include "simon.h"
00116 //#include "tim.h"
00117 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 //#include "helpdownload.h"
00119 
00120 using namespace InetUtils;
00121 
00122 CC_IMPLEMENT_DYNAMIC(AsynchDownload, CCObject);
00123 CC_IMPLEMENT_MEMDUMP(DownloadQueue, CCObject);
00124 CC_IMPLEMENT_MEMDUMP(DownloadCache, CCObject);
00125 CC_IMPLEMENT_MEMDUMP(InternetManager, CCObject);
00126 
00127 #define new CAM_DEBUG_NEW
00128 
00129 #define MAX_ATTEMPTS    5 // maximum no. of attempts made to download a file
00130 #define ICON_ANIMATION      101 // timer event for icon animation
00131 #define ICON_UPDATE_INTERVAL    300
00132 
00133 
00134 // static members
00135 CTypedPtrMap<CMapPtrToPtr, HWND, AsynchDownload::AsynchBindStatusCallback*> AsynchDownload::AsynchBindStatusCallback::m_CallbackTable; 
00136 HWND AsynchDownload::AsynchBindStatusCallback::m_hNormalPriorityProgressDlg = NULL;
00137 UINT32 AsynchDownload::AsynchBindStatusCallback::m_nNormalPriorityIconID = _R(IDI_DOWNLOADING);
00138 INT32 AsynchDownload::m_nNormalPriorityInstanceCount = 0;
00139 INT32 AsynchDownload::m_nHighPriorityInstanceCount = 0;
00140 HICON AsynchDownload::AsynchBindStatusCallback::m_hiconProgress = 0;
00141 
00142 // Matt 15/02/2001
00143 typedef BOOL (WINAPI *P_GDFSE)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
00144                                                   
00145                                                   
00147 // AsynchDownload
00149                                                   
00150 /********************************************************************************************
00151 
00152   > AsynchDownload::AsynchDownload(DOWNLOAD_HANDLE hDownload, LPDOWNLOADINFO pDownloadInfo)
00153 
00154     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00155     Created:    18/11/96
00156     Inputs:     hDownload - handle of download as assigned by the InternetManager
00157                     pDownloadInfo - pointer to DOWNLOADINFO struct
00158                                 
00159     Purpose:    Constructor
00160                      
00161     Notes:      This constructor is not meant to be called directly  - use InternetManager::RegisterDownload()
00162                     if you want to download a file
00163 
00164 ********************************************************************************************/
00165 
00166 AsynchDownload::AsynchDownload(DOWNLOAD_HANDLE hDownload, LPDOWNLOADINFO pDownloadInfo)
00167 {
00168     m_Handle = hDownload;
00169     m_bSuccess = FALSE;
00170     m_ulFileSize = 0;
00171     m_ulDownloaded = 0;
00172     m_nPercentageDownloaded = 0;
00173     m_bAbort = FALSE;
00174     m_pMoniker = NULL;
00175     m_pBindContext = NULL;
00176     m_pCallback = NULL;
00177     m_strTargetURL = pDownloadInfo->strURL;
00178     m_strLocalFilePath = pDownloadInfo->strLocalFile;
00179     m_nFileType = (FileType) pDownloadInfo->nFileType;
00180     m_nAttempts = 0;
00181     m_Priority = (Priority) pDownloadInfo->nPriority;
00182     m_bHasProgressDlg = pDownloadInfo->bHasProgressDlg;
00183     if (m_bHasProgressDlg = pDownloadInfo->bHasProgressDlg)
00184         m_strDescription = pDownloadInfo->strDescription;
00185     if (m_Priority == PRIORITY_NORMAL)
00186         m_nNormalPriorityInstanceCount++;
00187     else if (m_Priority == PRIORITY_HIGH)
00188         m_nHighPriorityInstanceCount++;
00189 
00190     m_hwndNotifyWindow = pDownloadInfo->hwndNotifyWindow;
00191     m_lNotifyToken = pDownloadInfo->lNotifyToken;
00192 
00193     AfxOleLockApp();
00194 }
00195 
00196 AsynchDownload::~AsynchDownload()
00197 {
00198     // Deallocate whatever resources were alocated during the binding operation
00199     Cleanup();
00200     if (m_Priority == PRIORITY_NORMAL)
00201         m_nNormalPriorityInstanceCount--;
00202     else if (m_Priority == PRIORITY_HIGH)
00203         m_nHighPriorityInstanceCount--;
00204     AfxOleUnlockApp();
00205 }
00206 
00207 
00208 
00209 /********************************************************************************************
00210 
00211   > HRESULT AsynchDownload::DoBind()
00212 
00213     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00214     Created:    18/11/96
00215     Inputs:     none 
00216     Return:     S_OK if successful, E_FAIL otherwise
00217     Purpose:    Binds asynchronously an URL moniker to local storage (a file in the Internet cache)
00218                     The function returns immediately an HRESULT indicating merely whether the bind was started successfuly,
00219                     or failed due to invalid data, insufficient memory, etc., not waiting for the bind to complete. The result of the
00220                     started bind operation is returned by AsynchBindCallback::OnStopBind().
00221                      
00222     
00223 ********************************************************************************************/
00224 
00225 
00226 
00227 HRESULT AsynchDownload::DoBind()
00228 {
00229     if (m_strTargetURL.IsEmpty() || m_strLocalFilePath.IsEmpty())
00230     {
00231         ERROR3("Invalid (empty) URL or file path");
00232         return E_FAIL;
00233     }
00234 #ifdef _DEBUG
00235     String_256 strErrorMsg(m_strTargetURL); // we keep a non-wide copy of the URL for error reporting purposes
00236 #endif
00237     WCHAR wchURL[INTERNET_MAX_PATH_LENGTH];
00238     MultiByteToWideChar(CP_ACP, 0,  (TCHAR*) m_strTargetURL, -1, wchURL, INTERNET_MAX_PATH_LENGTH);
00239     if (IsValidURL(NULL, wchURL, 0) == S_FALSE)
00240     {
00241 #ifdef _DEBUG
00242         strErrorMsg += _T(" is not a valid URL");
00243         ERROR3(strErrorMsg);
00244 #endif
00245         return E_FAIL;
00246     }
00247     IStream* pStream = NULL;
00248     m_pCallback = new AsynchBindStatusCallback(this);
00249     if (!m_pCallback)
00250     { 
00251         ERROR3("Memory allocation error");
00252         return E_OUTOFMEMORY;
00253     }
00254     HRESULT hr = CreateURLMoniker(NULL, wchURL, &m_pMoniker);
00255     if (FAILED(hr))
00256     {
00257         ERROR3("Could not create URL Moniker");
00258         Cleanup();
00259         return hr;
00260     }
00261     hr = CreateBindCtx(0, &m_pBindContext);
00262     if (FAILED(hr))
00263     {
00264         ERROR3("Could not create bind context");
00265         Cleanup();
00266         return hr;
00267     }
00268     hr = RegisterBindStatusCallback(m_pBindContext, m_pCallback, 0, 0L);
00269     if (FAILED(hr))
00270     {
00271         ERROR3("Failed to register callback object");
00272         Cleanup();
00273         return hr;
00274     }
00275     hr = m_pMoniker->BindToStorage(m_pBindContext, 0, IID_IStream, (void**)&pStream);
00276     m_nAttempts++;
00277     return hr;
00278 }
00279 
00280 
00281 
00282 /********************************************************************************************
00283 >   void AsynchDownload::Cleanup()
00284 
00285     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00286     Created:    18/11/96
00287     Inputs:         -
00288     Returns:    -
00289     Purpose:    Releases COM objects allocated during the binding process.
00290 
00291 
00292 ********************************************************************************************/
00293 void AsynchDownload::Cleanup()
00294 {
00295     if (m_pCallback)
00296     {
00297         m_pCallback->Release();
00298         m_pCallback = NULL;
00299     }
00300     if (m_pMoniker)
00301     {
00302         m_pMoniker->Release();
00303         m_pMoniker = NULL;
00304     }
00305     if (m_pBindContext)
00306     {
00307         m_pBindContext->Release();
00308         m_pBindContext = NULL;
00309     }
00310 }
00311 
00312 /********************************************************************************************
00313 >   HRESULT AsynchDownload::Retry()
00314 
00315     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00316     Created:    18/11/96
00317     Inputs:         -
00318     Returns:    - always S_OK               
00319 
00320     Purpose:    called in case of network error to retry the download
00321 
00322 ********************************************************************************************/
00323 HRESULT AsynchDownload::Retry()
00324 {
00325     if (m_nAttempts <= MAX_ATTEMPTS) // try another bind if we're allowed
00326     {
00327         TRACEUSER( "adrian", _T("\n\nRetrying the %d time to download %s\n\n"), m_nAttempts, (TCHAR*) m_strTargetURL);
00328         Cleanup();
00329         DoBind();
00330     }
00331     else // just inform the download manager that we're done
00332     {
00333         TRACEUSER( "adrian", _T("\n\nGiving up on %s\n\n"), (TCHAR*) m_strTargetURL);
00334         InternetManager::OnDownloadComplete(this);
00335     }
00336     return S_OK;
00337 }
00338 
00339 
00340 /********************************************************************************************
00341 >   HRESULT AsynchDownload::AbortDownload()
00342 
00343     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00344     Created:    18/11/96
00345     Inputs:         -
00346     Returns:    - S_OK if the download operation operation was successfully aborted.
00347                     - S_FALSE if the download was already aborted.
00348                     - E_FAIL if the download operation could not be aborted.
00349 
00350     Purpose:    cancels this download.
00351 
00352 
00353 ********************************************************************************************/
00354 
00355 
00356 HRESULT AsynchDownload::AbortDownload()
00357  {
00358     if (m_bAbort)
00359         return S_FALSE;
00360     else
00361         m_bAbort = TRUE;
00362     if (m_pCallback)
00363     {
00364         if (m_pCallback->m_pBinding)
00365             return m_pCallback->m_pBinding->Abort();
00366         else
00367             return E_FAIL;
00368     }
00369     return E_FAIL;
00370 }
00371 
00372 
00373 /********************************************************************************************
00374 >   void AsynchDownload::Release()
00375 
00376     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00377     Created:    18/11/96
00378     Inputs:         -
00379     Returns:    - 
00380 
00381     Purpose:    Provides a safe way to free memory and resources allocated to this download
00382                     Should be used in preference to the delete operator which may cause access
00383                     violations if the download is still in progress
00384 
00385 ********************************************************************************************/
00386 
00387 void AsynchDownload::Release()
00388 {
00389     if (m_pCallback && m_pCallback->m_pBinding)
00390         m_pCallback->m_pBinding->Abort();
00391     else
00392         delete this;
00393 }
00394 
00395 
00396 
00397 /********************************************************************************************
00398 >  AsynchDownload::AsynchBindStatusCallback::AsynchBindStatusCallback(AsynchDownload* pDownload)
00399 
00400     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00401     Created:    18/11/96
00402     Inputs:       pDownload     - pointer to the AsynchDownload object owning the callback
00403     Returns:    none
00404     Purpose:   callback object constructor.
00405 
00406 ********************************************************************************************/
00407 
00408 
00409 AsynchDownload::AsynchBindStatusCallback::AsynchBindStatusCallback(AsynchDownload* pDownload)
00410 {
00411     m_dwRef = 1;
00412     m_pDownload = pDownload;
00413     m_pBinding = NULL;
00414     m_hProgressDlg = NULL;
00415     m_nIconID = _R(IDI_DOWNLOADING); 
00416 }
00417 
00418 
00419 /********************************************************************************************
00420 >   AsynchDownload::AsynchBindStatusCallback::~AsynchBindStatusCallback()
00421 
00422     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00423     Created:    18/11/96
00424     Inputs:       none
00425     Returns:    none
00426     Purpose:   callback object destructor.
00427 
00428 ********************************************************************************************/
00429 
00430 AsynchDownload::AsynchBindStatusCallback::~AsynchBindStatusCallback()
00431 {
00432     if (m_pBinding) // download still in progress
00433     {
00434         // this will almost certainly result in an access violation
00435         ERROR3("AsynchDownload deleted while still in progress");
00436     }
00437 }
00438 
00439 /********************************************************************************************
00440 >   HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::QueryInterface(REFIID riid,void ** ppv)
00441 
00442     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00443     Created:    18/11/96
00444     Inputs:       riid  -   identifier of the requested interface
00445                      ppvObject  -   indirect pointer to the object
00446     Returns:    S_OK if the interface is supported, E_NOINTERFACE otherwise.
00447     Purpose:   standard IUnknown method
00448 
00449 ********************************************************************************************/
00450 HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::QueryInterface(REFIID riid,void ** ppv)
00451 {
00452     *ppv = NULL;
00453 
00454     if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
00455     {
00456         *ppv = this;
00457         AddRef();
00458         return S_OK;
00459     }
00460     else if (riid == IID_IAuthenticate)
00461     {
00462         *ppv = (IAuthenticate *)this;
00463         AddRef();
00464         return S_OK;
00465     }
00466 
00467     return E_NOINTERFACE;
00468 }  
00469 
00470 /********************************************************************************************
00471 >   HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding* pbinding)
00472 
00473     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00474     Created:    18/11/96
00475     Inputs:       dwReserved    -   not used, must be 0
00476                      pbinding   -   pointer to the IBinding interface for the current bind operation
00477     Returns: S_OK 
00478     Purpose:   standard IBindStatusCallback method (must be  implemented). Allows us to get a pointer to the binding. 
00479     See also:  MS Internet SDK documentation (URL Monikers)
00480 ********************************************************************************************/
00481 HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding* pbinding)
00482 {
00483     m_pDownload->m_ulFileSize = 0;
00484     m_pDownload->m_ulDownloaded = 0;
00485     // We save the pointer to the binding
00486     m_pBinding = pbinding;
00487     if (m_pBinding != NULL)
00488         m_pBinding->AddRef();
00489     // Create & display the progress dialog if the download requires one
00490     if (m_pDownload->m_bHasProgressDlg)
00491     {
00492         HWND hProgressDlg;
00493         BOOL bNeedsPositioning = (m_pDownload->m_Priority != PRIORITY_NORMAL) ||
00494                 (m_pDownload->m_Priority == PRIORITY_NORMAL && !m_hNormalPriorityProgressDlg);
00495         if (!AttachProgressDlg())
00496         {
00497             ERROR3("Could not create progress dialog");
00498             goto END; 
00499         }
00500         if (m_pDownload->m_Priority == PRIORITY_NORMAL)
00501             hProgressDlg = m_hNormalPriorityProgressDlg;
00502         else
00503             hProgressDlg = m_hProgressDlg;
00504         if (hProgressDlg)
00505         {
00506             HWND hProgressBar = GetDlgItem(hProgressDlg, _R(IDC_PROGRESSBAR)); // hadle of progress bar
00507             HWND hPercentage = GetDlgItem(hProgressDlg, _R(IDC_PERCENTAGE)); // handle of static text used to display percent downloaded
00508             ERROR3IF(!(hProgressBar && hPercentage), "Cannot find control");
00509             // Set initial position on the progress bar to 0
00510             ::SendMessage(hProgressBar, PBM_SETPOS, (WPARAM) 0, 0);
00511             // Set initial percentage
00512             String_256 strPercentage = _T("0%");
00513             // Set the dialog caption
00514             String_256 strURL = m_pDownload->m_strTargetURL;
00515             strtok(strURL, _T(":/"));
00516             String_256 strCaption;
00517             strCaption.MakeMsg(_R(IDS_CONTACTING_HOST), strtok(NULL, _T(":/")));
00518             ::SetWindowText(hProgressDlg, (TCHAR*) strCaption);
00519             ::SetWindowText(hPercentage, (TCHAR*) strPercentage);
00520             // Position the window if necessary 
00521             if (bNeedsPositioning)
00522             {
00523                 CRect rcDialog;
00524                 ::GetWindowRect(hProgressDlg, &rcDialog);
00525                 // Finally position & show the dialog box
00526                 ::SetWindowPos(hProgressDlg, HWND_TOPMOST, ::GetSystemMetrics(SM_CXSCREEN) - rcDialog.Width(), 0,
00527                         0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
00528             }
00529             else
00530                 ::SetWindowPos(hProgressDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
00531 
00532             ::UpdateWindow(hProgressBar);
00533             ::UpdateWindow(hPercentage);
00534             ::UpdateWindow(hProgressDlg);
00535         }
00536     }
00537 END:    
00538     return S_OK;
00539 }
00540 
00541 /********************************************************************************************
00542 >   HRESULT _stdcall  AsynchDownload::AsynchBindStatusCallback::OnProgress(UINT32 ulProgress, UINT32 ulProgressMax, UINT32 ulStateCode,
00543                             LPCWSTR pwzStateText)
00544 
00545     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00546     Created:    18/11/96
00547     Inputs:       ulProgress    -   progress indicator for bind operation
00548                      ulProgressMax  -   expected maximum value of ulProgress parameter
00549                      ulStateCode    -   BINDSTATUS value indicating bind progress
00550                      szStateText    -   displayable information indicating bind progress
00551     Returns:    NOERROR or E_ABORT if Abort() has been called 
00552     Purpose:   standard IBindStatusCallback (must be implemented). Allows monitoring the progress of the bind.
00553                     changes 
00554     See also:  MS Internet SDK documentation (URL Monikers)
00555 ********************************************************************************************/
00556 HRESULT _stdcall  AsynchDownload::AsynchBindStatusCallback::OnProgress(UINT32 ulProgress, UINT32 ulProgressMax, UINT32 ulStateCode,
00557                             LPCWSTR pwzStateText)
00558 {
00559     if (m_pDownload->m_bAbort)
00560     {
00561         return E_ABORT;
00562     }
00563     TCHAR   sz[256];
00564     if(pwzStateText != NULL)
00565         WideCharToMultiByte(CP_ACP, 0, pwzStateText, -1, sz, sizeof(sz)/sizeof(TCHAR), 0, 0);
00566     // Update download progress
00567     // (Note that ulProgressMax is the file size returned by the server and if may be 0 if the server doesn't know it)
00568     // (Hopefully our server is better than that)
00569     m_pDownload->m_ulFileSize = (ulProgress > ulProgressMax) ? ulProgress : ulProgressMax;
00570     m_pDownload->m_ulDownloaded = ulProgress;
00571     INT32 nPercentage = (ulProgress && ulProgressMax) ? (100 * ulProgress/ulProgressMax) : 0;
00572 
00573     if (nPercentage!=m_pDownload->m_nPercentageDownloaded)
00574     {
00575         // we should inform our client that things are happening...
00576         InternetManager::SetState(m_pDownload->m_Handle, AsynchDownload::STATE_PENDING);
00577     }
00578 
00579     m_pDownload->m_nPercentageDownloaded = nPercentage;
00580     if (m_pDownload->m_bHasProgressDlg)
00581     {
00582         HWND hProgressDlg;
00583         if (m_pDownload->m_Priority == PRIORITY_NORMAL)
00584             hProgressDlg = m_hNormalPriorityProgressDlg;
00585         else
00586             hProgressDlg = m_hProgressDlg;
00587         // Make sure the progress dialog handle is still valid before attempting to update it 
00588         if (hProgressDlg && ::IsWindow(hProgressDlg))
00589         {   
00590             HWND hPercentage = GetDlgItem(hProgressDlg, _R(IDC_PERCENTAGE)); // handle of static text used to display percent downloaded
00591             HWND hProgressBar = GetDlgItem(hProgressDlg, _R(IDC_PROGRESSBAR)); // handle of progress bar
00592             if (!(hPercentage && hProgressBar))
00593             {
00594                 ERROR3("Cannot find control");
00595             }
00596             else
00597             {
00598                 String_256 strCaption, strPercentage;
00599                 switch (ulStateCode)
00600                 {
00601                     case BINDSTATUS_FINDINGRESOURCE: // these 3 states are now displayed as "Contacting host..."
00602                     case BINDSTATUS_CONNECTING:
00603                     case BINDSTATUS_SENDINGREQUEST:
00604                         break;
00605 
00606                     case BINDSTATUS_DOWNLOADINGDATA:
00607                         {
00608                             strCaption = String_256(_R(IDS_DOWNLOAD));
00609                             if (m_pDownload->m_strDescription.IsEmpty()) // no description available, just use the filename
00610                             {
00611                                 strCaption+= _T("'");
00612                                 strCaption += strrchr(m_pDownload->m_strTargetURL, _T('/')) + 1;
00613                                 strCaption+= _T("'");
00614                             }
00615                             else
00616                             {
00617                                 strCaption += m_pDownload->m_strDescription;
00618                             }
00619                             if (!ulProgressMax)
00620                             {
00621                                 strPercentage = "N/A"; // in case the server didn't return any size information
00622                                 m_pDownload->m_nPercentageDownloaded = 0;
00623                             }
00624                             else // set the progress bar to the percentage we have downloaded
00625                             {
00626                                 wsprintf(strPercentage, "%d%%", nPercentage);
00627                                 ::SendMessage(hProgressBar, PBM_SETPOS, (WPARAM) nPercentage, 0);
00628                                 // Force an immediate repaint to avoid any redraw latency
00629                                 ::UpdateWindow(hProgressBar);
00630                             }
00631                         }
00632                         break;
00633                     case BINDSTATUS_ENDDOWNLOADDATA:
00634                         strPercentage = _T("100%");
00635                         ::SendMessage(hProgressBar, PBM_SETPOS, (WPARAM) 100, 0);
00636                         // Force an immediate repaint to avoid any redraw latency
00637                         ::UpdateWindow(hProgressBar);
00638                         strCaption = String_256(_R(IDS_ENDDOWNLOAD));
00639                         break;
00640                 }
00641                 // We'll update the controls only if the new strings are different to the current ones so that we don't 
00642                 // get any flicker 
00643                 TCHAR tchBuff[96];
00644                 ::GetWindowText(hPercentage, tchBuff, sizeof(tchBuff)/sizeof(TCHAR));
00645                 if (strPercentage != String_256(tchBuff))
00646                 {
00647                     ::SetWindowText(hPercentage, (TCHAR*) strPercentage);
00648                     ::UpdateWindow(hPercentage);
00649                 }
00650                 ::GetWindowText(hProgressDlg, tchBuff, sizeof(tchBuff)/sizeof(TCHAR));
00651                 if (strCaption.Length() && strCaption != String_256(tchBuff)) // we need to update the message
00652                 {
00653                     ::SetWindowText(hProgressDlg, (TCHAR*) strCaption);
00654                     ::UpdateWindow(hProgressDlg);
00655                 }
00656             }
00657         }
00658     }   
00659     // Save filename of local image if available
00660     if (ulStateCode == BINDSTATUS_CACHEFILENAMEAVAILABLE)
00661         m_pDownload->m_strCacheFileName = sz;
00662 
00663     return (NOERROR);
00664 }
00665 
00666 
00667 /********************************************************************************************
00668 >   HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnStopBinding(HRESULT hrState, LPCWSTR szError)
00669 
00670     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00671     Created:    18/11/96
00672     Inputs:       hrState   -       bind operation status code
00673                      szError    -   currently not used
00674     Returns:    S_OK 
00675     Purpose:   standard IBindStatusCallback (must be implemented). Allows us to retrieve the result of the bind in hrState.
00676     See also:  MS Internet SDK documentation (URL Monikers)
00677 ********************************************************************************************/
00678 
00679 HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnStopBinding(HRESULT hrState, LPCWSTR szError)
00680 {
00681 #ifdef _DEBUG
00682     if (hrState) // download diagnostic in debug builds
00683     {
00684         TCHAR szErrorMsg[256];
00685         if (hrState == 0x800C0006) // file not found
00686             wsprintf(szErrorMsg, "%s %s\n", (TCHAR*) (String_256) m_pDownload->m_strTargetURL, _T("not found on the server"));
00687         else
00688             wsprintf(szErrorMsg, "%s %s\n", _T("Got network error (possibly host not found) downloading"), (TCHAR*) (String_256) m_pDownload->m_strTargetURL);
00689          if (hrState != 0x80004004) // user cancel
00690              TRACE(szErrorMsg);
00691 
00692          // Write error to log
00693          String_256 strAppDataPath;
00694          GetAppDataPath(&strAppDataPath);
00695          PathName logPath(strAppDataPath);
00696          String_256 strLogName("errlog.txt");
00697          logPath.SetFileNameAndType(strLogName);
00698          static BOOL bShouldOverwrite = TRUE; // overwrite any error log generated in a previous session
00699          INT32 iFlags = bShouldOverwrite ? ios::out | ios::trunc    :   ios::out | ios::app;
00700          bShouldOverwrite = FALSE;
00701          ofstream logFile((TCHAR*) (String_256) logPath.GetPath(), iFlags);
00702          if (!logFile.bad())
00703              logFile << szErrorMsg;
00704          logFile.close();
00705     }
00706 #endif
00707 
00708     // Clean up
00709     if (m_pBinding)
00710     {
00711         m_pBinding->Release();
00712         m_pBinding = NULL;
00713     }
00714     if (m_pDownload->m_bHasProgressDlg)
00715         DetachProgressDlg(); // release/destroy the progress dialog, if any
00716 
00717     if (!hrState && !m_pDownload->m_bAbort) // file downloaded successfully
00718     {
00719         // The file has been downloaded to the system internet cache, so we need to copy it over to the required location
00720         PathNameEx path(m_pDownload->m_strLocalFilePath);
00721         BOOL bIsCacheFile = DownloadCache::IsCachePath(m_pDownload->m_strLocalFilePath); 
00722         if (!path.CreateLocation()) // create the directory path if it doesn't exist - otherwise ofstream() may fail
00723         {
00724             InformError(_R(IDS_DIRCREATEFAIL), _R(IDS_OK));
00725             m_pDownload->m_bSuccess = FALSE;
00726             goto COMPLETE;
00727         }
00728         ifstream stmCache((TCHAR*) m_pDownload->m_strCacheFileName, ios::in | ios::nocreate | ios::binary);
00729         // We overwrite the existing file if any
00730         DownloadCache::RemoveFile(m_pDownload->m_strLocalFilePath);
00731         if (bIsCacheFile)
00732             DownloadCache::m_CacheMonitor.IgnoreEvents(1);
00733         ofstream stmFile((TCHAR*) m_pDownload->m_strLocalFilePath, ios::out | ios::binary | ios::trunc);
00734         if (stmCache.bad() || stmFile.bad())
00735         {
00736             String_256 strTemp;
00737             PathName path(m_pDownload->m_strLocalFilePath);
00738             strTemp.MakeMsg(_R(IDS_FILEIOERROR), (TCHAR*) String_256(path.GetFileName()));
00739             Error::SetError(0, strTemp, 0);
00740             InformError();
00741             stmFile.close();
00742             stmCache.close();
00743             goto COMPLETE;
00744         }
00745         stmFile << stmCache.rdbuf();
00746         if (stmFile.bad() || stmCache.bad())
00747         {
00748             String_256 strTemp;
00749             PathName path(m_pDownload->m_strLocalFilePath);
00750             strTemp.MakeMsg(_R(IDS_FILEIOERROR), (TCHAR*) String_256(path.GetFileName()));
00751             Error::SetError(0,strTemp, 0);
00752             InformError();
00753         }
00754         else
00755             m_pDownload->m_bSuccess = TRUE;
00756         stmFile.close();
00757         stmCache.close();
00758     }
00759     else if (hrState == 0x800C0007) // we got a network error (unfortunately these error codes are not documented by Microsoft)
00760     {
00761         return m_pDownload->Retry();
00762     }
00763          
00764 COMPLETE:
00765     InternetManager::OnDownloadComplete(m_pDownload);
00766     return S_OK;
00767 }  
00768 
00769 /********************************************************************************************
00770 >   HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
00771 
00772     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00773     Created:    18/11/96
00774     Inputs:       pgrfBINDF -   pointer to BINDF value
00775                      pbindinfo  -   pointer to BINDINFO structure
00776     Returns:    S_OK 
00777     Purpose:   standard IBindStatusCallback method (must be implemented)
00778     See also:  MS Internet SDK documentation (URL Monikers)
00779 ********************************************************************************************/
00780 HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
00781 {
00782     *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_GETNEWESTVERSION; // bind asynchronously
00783     pbindInfo->cbSize = sizeof(BINDINFO);
00784     pbindInfo->szExtraInfo = NULL;
00785     memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM));
00786     pbindInfo->grfBindInfoF = 0;
00787     pbindInfo->dwBindVerb = BINDVERB_GET;
00788     pbindInfo->szCustomVerb = NULL;
00789     return S_OK;
00790 } 
00791 
00792 /********************************************************************************************
00793 > HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
00794                             STGMEDIUM* pstgmed)
00795 
00796     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00797     Created:    18/11/96
00798     Inputs:       gfBSCF    -   BSCF enumeration values
00799                      dwSize -   length of data in bytes available from current bind operation
00800                      pfmtetc    -   pointer to FORMATETC structure
00801                      pstgmed    -   Pointer to STGMEDIUM structure
00802     Returns:    S_OK 
00803     Purpose:   standard IBindStatusCallback method (must be implemented, so we just return S_OK)
00804     See also:  MS Internet SDK documentation (URL Monikers)
00805 ********************************************************************************************/
00806 HRESULT _stdcall AsynchDownload::AsynchBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
00807                             STGMEDIUM* pstgmed)
00808 {
00809     // We're not reading any data before the download is complete, so just return S_OK 
00810     return S_OK;
00811 }  
00812 
00813 /********************************************************************************************
00814 >   STDMETHODIMP AsynchDownload::AsynchBindStatusCallback::Authenticate(HWND *phwnd,
00815                                                     LPWSTR *pszUserName, LPWSTR *pszPassword)
00816     Author:     Jonathan_Payne (Xara Group Ltd) <camelotdev@xara.com>
00817     Created:    14/02/2001
00818     Purpose:    This method is called when athentication is required to access a resource.
00819                 We simply tell the calling function to go away and the authentication it's
00820                 self.
00821     See also:   MSDN docs for the function
00822 ********************************************************************************************/
00823 STDMETHODIMP AsynchDownload::AsynchBindStatusCallback::Authenticate(HWND *phwnd, LPWSTR *pszUserName, LPWSTR *pszPassword)
00824 {
00825     *phwnd = AfxGetMainWnd()->m_hWnd;
00826     *pszUserName = 0;
00827     *pszPassword = 0;
00828 
00829     return S_OK;
00830 }
00831 
00832 /********************************************************************************************
00833 > BOOL CALLBACK AsynchDownload::AsynchBindStatusCallback::DialogProc(HWND hwndDlg, UINT32 message, WPARAM wParam, LPARAM lParam)
00834 
00835     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00836     Created:    18/11/96
00837     Inputs:      hwndDlg - identifies the dialog box. 
00838                     uMsg - specifies the message. 
00839                     wParam - specifies additional message-specific information. 
00840                     lParam - specifies additional message-specific information
00841 
00842     Returns:    non-zero if it processes the message, 0 if not
00843     Purpose:   progress dialog procedure
00844 
00845 ********************************************************************************************/
00846 
00847 BOOL CALLBACK AsynchDownload::AsynchBindStatusCallback::DialogProc(HWND hDlg, UINT32 message, WPARAM wParam, LPARAM lParam)
00848 {
00849     switch (message)
00850      {
00851         case WM_TIMER:
00852         {
00853             if (wParam == ICON_ANIMATION)
00854             {
00855                 UINT32* pIconID = NULL;
00856                 if (hDlg == m_hNormalPriorityProgressDlg)
00857                     pIconID = &m_nNormalPriorityIconID;
00858                 else
00859                 {
00860                     AsynchBindStatusCallback* pCallback = NULL;
00861                     if (!m_CallbackTable.Lookup(hDlg, pCallback))
00862                     {
00863                         ERROR3("Callback not found in the lookup table");
00864                     }
00865                     else if (pCallback)
00866                         pIconID = &pCallback->m_nIconID;
00867                 }
00868                 if (*pIconID == _R(IDI_DOWNLOADING6))
00869                     *pIconID = _R(IDI_DOWNLOADING);
00870                 else
00871                     (*pIconID)++;
00872 
00873                 HICON hiconPrev = m_hiconProgress;
00874                 m_hiconProgress = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(*pIconID), IMAGE_ICON, 16, 16, 0);
00875                 ::SendMessage(hDlg, WM_SETICON, (WPARAM) FALSE, (LPARAM) m_hiconProgress );
00876                 ::DestroyIcon(hiconPrev);
00877                 return TRUE;
00878             }
00879         }
00880         break;
00881         case WM_COMMAND:
00882         {
00883             switch (LOWORD(wParam))
00884             {
00885                 case IDCANCEL:
00886                 {
00887 CANCEL:                 
00888                     if (m_hiconProgress)
00889                     {
00890                         ::DestroyIcon(m_hiconProgress);
00891                         m_hiconProgress = 0;
00892                     }
00893 
00894                     // Got CANCEL from the user, find the callback this dialog belongs to
00895                     if (hDlg == m_hNormalPriorityProgressDlg) // thumbnail/catalog file download progress
00896                     {
00897                         AsynchDownload* pDownload = InternetManager::GetCurrentNormalPriorityDownload();
00898                         if (pDownload)
00899                             pDownload->AbortDownload();
00900                         return TRUE;
00901                     }
00902                     AsynchBindStatusCallback* pCallback = NULL;
00903                     if (!m_CallbackTable.Lookup(hDlg, pCallback))
00904                     {
00905                         ERROR3("Callback not found in the lookup table");
00906                     }
00907                     else
00908                     {
00909                         if (pCallback && pCallback->m_pDownload)
00910                         {
00911                             // Although the callback object doesn't contain any runtime information and
00912                             // thus we cannot directly check if it's still valid or not, a non-null pointer in the callback
00913                             // table should guarantee that, as callback objects call DetachProgressDlg() when
00914                             // a download completes or is terminated by the user.
00915                             // Never use the delete operator on a download in progress (instead of calling the Release() member
00916                             // function) as it could result in pCallback being no longer valid at this point and cause an access violation
00917                             HRESULT result = pCallback->m_pDownload->AbortDownload();
00918                             ERROR3IF(result == E_FAIL, "Could not abort download");
00919                         }
00920                     }
00921                     return TRUE;
00922                 }
00923                 break;
00924             }
00925         }
00926         break;
00927         case WM_CLOSE:
00928             goto CANCEL; // we assume they actually want to cancel the download
00929     }
00930     return FALSE;
00931 }
00932 
00933 
00934 /********************************************************************************************
00935 > BOOL AsynchDownload::AsynchBindStatusCallback::AttachProgressDlg()
00936 
00937     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
00938     Created:    18/11/96
00939     Inputs:      -
00940     Returns:    TRUE if the dialog box was created successful, FALSE otherwise
00941     Purpose:   creates a modeless progress dialog box and attach it to this callback,
00942                     i.e. make an entry in the callback table mapping the callback pointer 
00943                     to the handle of its dialog
00944 
00945 ********************************************************************************************/
00946 BOOL AsynchDownload::AsynchBindStatusCallback::AttachProgressDlg()
00947 {
00948     static BOOL bShouldRegisterClass = TRUE;
00949     if (bShouldRegisterClass) // register the progress dlg class if needed
00950     {
00951         WNDCLASSEX wndClass;
00952         static char szClassName[] =  "DownloadProgressDlg";
00953         if (!::GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wndClass))
00954         {
00955             ERROR3("Failed to get class info");
00956         }
00957         wndClass.lpszClassName = szClassName;
00958         wndClass.hInstance = AfxGetResourceHandle();
00959         wndClass.hIcon = NULL;
00960         wndClass.hIconSm = (HICON) ::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(_R(IDI_DOWNLOADING)), IMAGE_ICON, 16, 16, 0);
00961         wndClass.cbSize = sizeof(wndClass);
00962         ERROR3IF(!wndClass.hIconSm, "Failed to load icon");
00963         if (!::RegisterClassEx(&wndClass))
00964         {
00965             ERROR3("Failed to register progress dlg class");
00966         }
00967         else
00968             bShouldRegisterClass = FALSE;
00969     }
00970     if (m_pDownload->m_Priority == PRIORITY_HIGH) // high priority download - gets its own progress dialog
00971     {
00972         m_hProgressDlg = ::CreateDialogParam(AfxGetResourceHandle(), MAKEINTRESOURCE(_R(IDD_PROGRESS)), 
00973             HWND_DESKTOP,   (DLGPROC) &DialogProc, NULL);
00974         if (!m_hProgressDlg)
00975         {
00976             ERROR3("Could not create progress dialog");
00977             return FALSE;
00978         }
00979         else
00980         {
00981             // Enter the handle in the callback table
00982             m_CallbackTable[m_hProgressDlg] = this;
00983             ::SetTimer(m_hProgressDlg, ICON_ANIMATION, ICON_UPDATE_INTERVAL, NULL);
00984         }
00985     }
00986     else if (!m_hNormalPriorityProgressDlg) // low priority download - create dialog only if there is none to be reused
00987     {
00988         m_hNormalPriorityProgressDlg = ::CreateDialogParam(AfxGetResourceHandle(), MAKEINTRESOURCE(_R(IDD_PROGRESS)), 
00989             HWND_DESKTOP,   (DLGPROC) &DialogProc, NULL);
00990         if (!m_hNormalPriorityProgressDlg)
00991         {
00992             ERROR3("Could not create progress dialog");
00993             return FALSE;
00994         }
00995         else
00996             ::SetTimer(m_hNormalPriorityProgressDlg, ICON_ANIMATION, ICON_UPDATE_INTERVAL, NULL);
00997     }
00998     return TRUE;
00999 }
01000 
01001 
01002 /********************************************************************************************
01003 > void AsynchDownload::AsynchBindStatusCallback::DetachProgressDlg()
01004 
01005     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01006     Created:    18/11/96
01007     Inputs:      -
01008     Returns:    -
01009     Purpose:   called by OnStopBinding() to remove this callback's entry in the 
01010                     callback table - as it will be no longer valid - and destroy the 
01011                     progress dialog if one exists
01012 
01013 ********************************************************************************************/
01014 void AsynchDownload::AsynchBindStatusCallback::DetachProgressDlg()
01015 {
01016     if (m_hProgressDlg && m_pDownload->m_Priority == PRIORITY_HIGH)
01017     {
01018         BOOL bDestroyed = FALSE;
01019         // Kill the icon animation timer
01020         bDestroyed = ::KillTimer(m_hProgressDlg, ICON_ANIMATION);
01021         ERROR3IF(!bDestroyed, "Warning: timer not destroyed");
01022         // Make sure the progress dialog is destroyed 
01023         if (::IsWindow(m_hProgressDlg))
01024             bDestroyed = ::DestroyWindow(m_hProgressDlg);
01025         ERROR3IF(!bDestroyed, "Warning: progress dialog not destroyed");
01026         // Just in case we couldn't destroy the window for some reason we'll set the dialog handle
01027         // entry in the callback table to NULL indicating it references a callback pointer no longer valid
01028         m_CallbackTable[m_hProgressDlg] = NULL;
01029         m_hProgressDlg = NULL;
01030     }
01031     else if (m_hNormalPriorityProgressDlg && m_pDownload->m_Priority == PRIORITY_NORMAL)
01032     {
01033         if (m_pDownload->m_nNormalPriorityInstanceCount == 1)
01034         {
01035             // Kill the icon animation timer
01036             BOOL bDestroyed = ::KillTimer(m_hNormalPriorityProgressDlg, ICON_ANIMATION);
01037             ERROR3IF(!bDestroyed, "Warning: timer not destroyed");
01038             ::DestroyWindow(m_hNormalPriorityProgressDlg);
01039             m_hNormalPriorityProgressDlg = NULL;
01040         }
01041     }
01042 }
01043 
01044 
01045 
01047 // DownloadQueue
01049 
01050 
01051 /********************************************************************************************
01052 > DownloadQueue::~DownloadQueue()
01053 
01054     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01055     Created:    18/11/96
01056     Inputs:       none
01057     Returns:    none 
01058     Purpose:   destructor
01059 ********************************************************************************************/
01060 
01061 DownloadQueue::~DownloadQueue()
01062 {
01063     if (!IsEmpty())
01064         Flush();
01065 }
01066 
01067 
01068 
01069 /********************************************************************************************
01070 > DownloadQueue::Queue(AsynchDownload* pDownload)
01071 
01072     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01073     Created:    18/11/96
01074     Inputs:       pDownload     -   pointer to download to be queued
01075     Returns:    TRUE is successful, FALSE otherwise
01076     Purpose:   Adds a new download  pointer to the queue
01077 ********************************************************************************************/
01078 
01079 
01080 BOOL DownloadQueue::Queue(AsynchDownload* pDownload)
01081 {
01082     if (!pDownload)
01083     {
01084         ERROR3("Unexpected NULL pointer");
01085         return FALSE;
01086     }
01087     if (m_enType != LIFO && m_enType != FIFO)
01088     {
01089         ERROR3("Invalid queue type");
01090         return FALSE;
01091     }
01092     try
01093     {
01094         if (m_enType == FIFO)
01095             m_List.AddTail(pDownload);
01096         else
01097             m_List.AddHead(pDownload);
01098     }
01099     catch (CMemoryException* pxMem)
01100     {
01101         pxMem->Delete();
01102         ERROR2(FALSE, "Could not queue download - memory problems");
01103     }
01104     return TRUE;
01105 }
01106 
01107 
01108 /********************************************************************************************
01109 > DownloadQueue::Remove(AsynchDownload* pDownload)
01110 
01111     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01112     Created:    18/11/96
01113     Inputs:       pDownload     -   pointer to download to be removed
01114     Returns:    TRUE is successful, FALSE otherwise
01115     Purpose:   remove a download  from the queue. The object is also deleted
01116 ********************************************************************************************/
01117 
01118 
01119 BOOL DownloadQueue::Remove(AsynchDownload* pDownload)
01120 {
01121     if (!pDownload)
01122     {
01123         ERROR3("Unexpected NULL pointer");
01124         return FALSE;
01125     }
01126     POSITION pos = m_Lis