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_List.Find(pDownload);
01127     if (!pos)
01128         return FALSE; // the object is not is this queue
01129     m_List.RemoveAt(pos);
01130     pDownload->Release();
01131     return TRUE;
01132 }
01133 
01134 
01135 /********************************************************************************************
01136 > DownloadQueue::Flush()
01137 
01138     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01139     Created:    18/11/96
01140     Inputs:       pDownload     -   pointer to download to be removed
01141     Returns:    none
01142     Purpose:    Flushes the queue and deletes the objects at the same time    
01143 ********************************************************************************************/
01144 
01145 void DownloadQueue::Flush()
01146 {
01147     if (IsEmpty())
01148         return;
01149     POSITION pos = m_List.GetHeadPosition();
01150     while (pos)
01151     {
01152         AsynchDownload* pDownload = m_List.GetNext(pos);
01153         if (pDownload)
01154             pDownload->Release();
01155     }
01156     m_List.RemoveAll();
01157 }
01158 
01159 
01160 
01161 /********************************************************************************************
01162 > AsynchDownload* DownloadQueue::FindDownload(const String_256& strFileName)
01163 
01164     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01165     Created:    18/11/96
01166     Inputs:       strFileName       -   local filename of the download searched for
01167     Returns:    pointer to AsynchDownload object if found, NULL otherwise
01168     Purpose:    searches the queue for an object downloading to a known local file    
01169 ********************************************************************************************/
01170 
01171 AsynchDownload* DownloadQueue::FindDownload(const String_256& strFileName)
01172 {
01173     if (IsEmpty())
01174         return NULL;
01175     String_256 strLocalFileName(strFileName);
01176     AsynchDownload* pIterator = NULL;
01177     POSITION pos = m_List.GetHeadPosition();
01178     while (pos)
01179     {
01180         pIterator = m_List.GetNext(pos);
01181         if (!strLocalFileName.CompareTo(pIterator->GetLocalFileName(), FALSE))
01182             return pIterator;
01183     }
01184     return NULL;
01185 }
01186 
01187 
01188 /********************************************************************************************
01189 > AsynchDownload* DownloadQueue::FindDownload(DOWNLOAD_HANDLE hDownload)
01190 
01191     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01192     Created:    18/11/96
01193     Inputs:       hDownload     -   handle of download searched for
01194     Returns:    pointer to AsynchDownload object if found, NULL otherwise
01195     Purpose:    searches the queue for an object wirh a known handle    
01196 ********************************************************************************************/
01197 
01198 
01199 
01200 AsynchDownload* DownloadQueue::FindDownload(DOWNLOAD_HANDLE hDownload)
01201 {
01202     AsynchDownload* pDownload = NULL;
01203     if (IsEmpty())
01204         return pDownload;
01205     AsynchDownload* pIterator = NULL;
01206     POSITION pos = m_List.GetHeadPosition();
01207     while (pos && !pDownload)
01208     {
01209         pIterator = m_List.GetNext(pos);
01210         if (pIterator->GetHandle() == hDownload)
01211             pDownload = pIterator;
01212     }
01213     return pDownload;
01214 }
01215 
01216 
01217 
01218 
01219 /********************************************************************************************
01220 > AsynchDownload* DownloadQueue::GetNextDownload()
01221 
01222     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01223     Created:    18/11/96
01224     Inputs:       none
01225     Returns:    pointer to AsynchDownload object if any waiting in the queue, NULL otherwise
01226     Purpose:    access the next object in the queue (which is removed from the queue at the same time)       
01227 ********************************************************************************************/
01228 
01229 
01230 
01231 AsynchDownload* DownloadQueue::GetNextDownload()
01232 {
01233     AsynchDownload* pDownload = NULL;
01234     if (m_List.IsEmpty())
01235         return pDownload;
01236     POSITION pos = m_List.GetHeadPosition();
01237     pDownload = m_List.GetAt(pos);
01238     m_List.RemoveAt(pos); // remove the download from the queue
01239     return pDownload;
01240 }
01241 
01242 
01243 
01245 // DownloadCache
01247 
01248 UINT32 DownloadCache::m_lMaxSize = 5000 * SIZEOFKILOBYTE; // initial cache size
01249 STL::priority_queue< CacheEntry, stl::vector<CacheEntry>, CacheRemovalAlgorithm> DownloadCache::m_CacheData;
01250 STL::priority_queue< CacheEntry, stl::vector<CacheEntry>, CacheRemovalAlgorithm> DownloadCache::m_TemporaryCacheData;
01251 INT32 DownloadCache::m_nInstanceCount = 0;
01252 UINT32 DownloadCache::m_lCurrentSize = 0;
01253 String_256 DownloadCache::m_strCachePath;
01254 DownloadCache::CacheMonitor DownloadCache::m_CacheMonitor;
01255 HANDLE DownloadCache::CacheMonitor::rgEvents[2]; 
01256 volatile INT32 DownloadCache::CacheMonitor::m_nIgnoreCount;
01257 CRITICAL_SECTION DownloadCache::CacheMonitor::m_CacheDataCriticalSection;
01258 
01259 
01260 
01261 /********************************************************************************************
01262 > BOOL DownloadCache::SetPath(const String_256& rCachePath)
01263 
01264     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01265     Created:    17/06/97
01266     Inputs:       - rCachePath - reference to string containing the full path of the cache root directory
01267                         If the directory does not exist, it will be created
01268     Returns:    - TRUE if succeeed, FALSE otherwise
01269     Purpose:    - allocate a directory on the hard disk containing the files that the cache is to manage
01270 ********************************************************************************************/
01271 
01272 
01273 BOOL DownloadCache::SetPath(const String_256& rCachePath)
01274 {
01275     m_strCachePath = rCachePath;
01276     // if the directory does not exist, we create it
01277     if (_taccess(m_strCachePath, 0) == -1)
01278     {
01279         PathNameEx cachePath(m_strCachePath);
01280         if (!cachePath.CreateLocation())
01281             return FALSE;
01282         else
01283         {
01284             while (!m_CacheData.empty())
01285                 m_CacheData.pop();
01286             m_lCurrentSize = 0; // the cache is currently empty
01287             return TRUE;
01288         }
01289     }
01290     else
01291     {
01292         Refresh(); // update cache data 
01293         return TRUE;
01294     }
01295 #ifdef _DEBUG
01296     AssertCacheDataValid();
01297 #endif
01298 }
01299 
01300 
01301 
01302 
01303 /********************************************************************************************
01304 > BOOL DownloadCache::SetSize(UINT32 lSize)
01305 
01306     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01307     Created:    17/06/97
01308     Inputs:       - lSize - new cache size 
01309     Returns:    - TRUE if succeeed, FALSE otherwise
01310     Purpose:    - modify cache size
01311 ********************************************************************************************/
01312 
01313 BOOL DownloadCache::SetSize(UINT32 lSize)
01314 {
01315     ERROR2IF(m_strCachePath.IsEmpty(), FALSE, "Invalid cache path");
01316 
01317     // ***********************************************************************
01318     // Matt - 15/02/2001
01319     // Modified so that large hard disks don't report incorrect disk space...
01320     // ***********************************************************************
01321 
01322     TCHAR szDrive[_MAX_DRIVE + 1];
01323     _tsplitpath(m_strCachePath, szDrive, NULL, NULL, NULL);
01324     camStrcat(szDrive, _T("\\"));
01325 
01326     // Get pointer to the GetDiskFreeSpaceEx function if it exists on this platform...
01327     BOOL fResult = FALSE;
01328     P_GDFSE pGetDiskFreeSpaceEx = NULL;
01329     pGetDiskFreeSpaceEx = (P_GDFSE)GetProcAddress(GetModuleHandle ("kernel32.dll"), "GetDiskFreeSpaceExA");
01330 
01331     unsigned __int64 i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
01332     DWORD dwSectorsPerCluster, dwBytesPerSector, dwNumberOfFreeClusters, dwTotalNumberOfClusters;
01333 
01334     if (pGetDiskFreeSpaceEx)
01335     {
01336         fResult = pGetDiskFreeSpaceEx(szDrive, (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes, (PULARGE_INTEGER)&i64FreeBytes);
01337 
01338         // Test to see if our requested cache size is larger than the current available space !!!
01339         if (((__int64)lSize >= i64FreeBytesToCaller) || (lSize < 500 * SIZEOFKILOBYTE)) { return FALSE; }
01340     }
01341     else
01342     {
01343         fResult = GetDiskFreeSpace (szDrive, &dwSectorsPerCluster, &dwBytesPerSector, &dwNumberOfFreeClusters, &dwTotalNumberOfClusters);
01344         if (fResult)
01345         {
01346             // Force 64bit maths so that drives over 4Gb work properly !!! 
01347             i64FreeBytes = (__int64)dwNumberOfFreeClusters * dwSectorsPerCluster * dwBytesPerSector;
01348 
01349             // Test to see if our requested cache size is larger than the current available space !!!
01350             if (((__int64)lSize >= i64FreeBytes) || (lSize < 500 * SIZEOFKILOBYTE)) { return FALSE; }
01351         }
01352     }
01353 
01354     // Just incase we failed to call either of the above functions...
01355     if (!fResult) { ERROR3("Can't GetDiskFreeSpace()!"); return FALSE; }
01356 
01357     // Set the preference value to the new value
01358     InternetManager::g_CacheSize = lSize;
01359 /*  
01360     // This code is an abortion and unmaintainable. Why not use the preference system?
01361     HKEY arhKeys[6];
01362     DWORD dwDisposition;
01363     memset(arhKeys, 0, sizeof(arhKeys));
01364     String_256 strProgramKey = GetProgramNameRegistryKey();
01365     BOOL bResult = ((RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &arhKeys[0]) == ERROR_SUCCESS) &&
01366         (RegCreateKeyEx(arhKeys[0], _tcstok(strProgramKey, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[1], &dwDisposition) == ERROR_SUCCESS) &&
01367         (RegCreateKeyEx(arhKeys[1], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[2], &dwDisposition) == ERROR_SUCCESS) &&
01368         (RegCreateKeyEx(arhKeys[2], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[3], &dwDisposition) == ERROR_SUCCESS) &&
01369         (RegCreateKeyEx(arhKeys[3], _T("Options"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[4], &dwDisposition) == ERROR_SUCCESS) &&
01370         (RegCreateKeyEx(arhKeys[4], _T("Internet"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[5], &dwDisposition) == ERROR_SUCCESS) &&
01371         (RegSetValueEx(arhKeys[5], _T("Cache Size"), NULL, REG_DWORD, (LPBYTE) &lSize,  sizeof(lSize)) == ERROR_SUCCESS));
01372     for (INT32 i = 5; i >= 0; i--) RegCloseKey(arhKeys[i]);
01373     ERROR3IF(!bResult, "Failed to save changes to registry");
01374 */
01375     
01376     m_lMaxSize = lSize;
01377     while (m_lCurrentSize > m_lMaxSize && m_CacheData.size())  // discard old files until we are within the cache limit
01378     {
01379         if (!_tremove(m_CacheData.top().szFilePath))
01380         {
01381             m_lCurrentSize -= m_CacheData.top().Size();
01382             TRACEUSER( "adrian", _T("Discarded %s - new cache size is %u bytes\n"), m_CacheData.top().szFilePath, m_lCurrentSize);
01383             TRACEUSER( "adrian", _T("New cache usage is %d percent\n"), GetUsage());
01384         }
01385         m_CacheData.pop();
01386     }
01387 #ifdef _DEBUG
01388     AssertCacheDataValid();
01389 #endif
01390     return TRUE;
01391 }
01392 
01393     
01394 
01395 /********************************************************************************************
01396 > void DownloadCache::Flush()
01397 
01398     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01399     Created:    17/06/97
01400     Inputs:       - 
01401     Returns:    - 
01402     Purpose:    - empty the cache
01403 
01404 ********************************************************************************************/
01405 
01406 void DownloadCache::Flush()
01407 {
01408     if (m_strCachePath.IsEmpty())
01409         return; 
01410     Traverse(m_strCachePath, TRUE);
01411 }
01412 
01413 
01414 
01415 /********************************************************************************************
01416 > void DownloadCache::Refresh()
01417 
01418     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01419     Created:    17/06/97
01420     Inputs:       - 
01421     Returns:    - 
01422     Purpose:    - update cache data - should be called either when the root has 
01423                         changed or when files in the cache have been modified outside
01424                         the program (ex. the user deleted some of the files, etc)
01425 ********************************************************************************************/
01426 
01427 
01428 void DownloadCache::Refresh()
01429 {
01430     if (m_strCachePath.IsEmpty())
01431     {
01432         ERROR3("Invalid cache path!");
01433         return;
01434     }
01435     ::EnterCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01436     __try
01437     {
01438         while (!m_CacheData.empty())
01439             m_CacheData.pop();
01440         m_lCurrentSize = 0;
01441         if (!Traverse(m_strCachePath))
01442         {
01443             ERROR3("Unknown cache error");
01444             __leave;
01445         }
01446         TRACEUSER( "adrian", _T("Current cache size is %u bytes\n"), m_lCurrentSize);
01447         TRACEUSER( "adrian", _T("Cache usage is %d percent\n"), GetUsage());
01448         if (!m_lMaxSize)
01449         {
01450             ERROR3("Cache size is 0!");
01451             __leave;
01452         }
01453         while (m_lCurrentSize > m_lMaxSize && m_CacheData.size())  // discard old files until we are within the cache limit
01454         {
01455             m_CacheMonitor.IgnoreEvents(1);
01456             if (!_tremove(m_CacheData.top().szFilePath))
01457             {
01458                 m_lCurrentSize -= m_CacheData.top().Size();
01459                 TRACEUSER( "adrian", _T("Discarded %s - new cache size is %u bytes\n"), m_CacheData.top().szFilePath, m_lCurrentSize);
01460                 TRACEUSER( "adrian", _T("New cache usage is %d percent\n"), GetUsage());
01461             }
01462             else
01463                 m_CacheMonitor.IgnoreEvents(-1);
01464             m_CacheData.pop();
01465         }
01466     #ifdef _DEBUG
01467         AssertCacheDataValid();
01468     #endif
01469     }
01470     __finally
01471     {
01472         ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01473     }
01474 }
01475 
01476 
01477 /********************************************************************************************
01478 > BOOL DownloadCache::Traverse(const String_256& strPath, BOOL bFlush = FALSE)
01479 
01480     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01481     Created:    17/06/97
01482     Inputs:       - strDirPath - full path of directory tree to traverse
01483                     - bFlush - TRUE if we also want to flush the cache as we traverse it
01484     Returns:    - TRUE if successful, FALSE otherwise
01485     Purpose:    - walk the named directory tree collecting information about its contents
01486 ********************************************************************************************/
01487 
01488  BOOL DownloadCache::Traverse(const String_256& strDirPath, BOOL bFlush)
01489  {
01490         ::EnterCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01491         _finddata_t findData;
01492         String_256 strSearchPattern(strDirPath);
01493         if (strSearchPattern[strSearchPattern.Length() -1] == _T('\\'))
01494             strSearchPattern += _T('*');
01495         else
01496             strSearchPattern += _T("\\*"); // add wildcard
01497         INT32 hSearch = _tfindfirst(strSearchPattern, &findData);
01498         if (hSearch == -1)
01499             return TRUE; // the directory is empty
01500         do
01501         {
01502             if (!(camStrcmp(findData.name, _T(".")) &&  camStrcmp(findData.name, _T(".."))))
01503                 continue; // skip this directory (.) or its parent (..)
01504             String_256 strFilePath(strDirPath);
01505             if (strFilePath[strFilePath.Length() -1] != _T('\\'))
01506                 strFilePath += _T('\\');
01507             strFilePath += findData.name;
01508             strFilePath.toLower();
01509             CacheEntry entry(strFilePath);
01510             if (!entry.IsValid())
01511             {
01512                 ERROR3("Invalid cache entry");
01513                 ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01514                 return FALSE;
01515             }
01516             BOOL bIsFolder = entry.IsFolder();
01517             BOOL bIsFile = entry.IsFile();
01518             if (entry.IsFolder()) 
01519                 Traverse(strFilePath, bFlush);
01520             else if (entry.IsFile()) 
01521             {
01522                 if (!bFlush)
01523                 {
01524                     m_CacheData.push(entry);
01525                     m_lCurrentSize += entry.Size();
01526                 }
01527                 else if (!camStrstr(strFilePath, _T(".txt")))
01528                     RemoveFile(strFilePath);
01529             }
01530         }
01531         while (_tfindnext(hSearch, &findData) == 0);
01532         _findclose(hSearch);
01533         ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01534         return TRUE;
01535  }
01536 
01537 
01538  /********************************************************************************************
01539  > BOOL DownloadCache::IsCachePath(const TCHAR* szPath);
01540 
01541     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01542     Created:    17/06/97
01543     Inputs:       - szPath - full path of file/folder
01544     Returns:    - TRUE if the path points into the cache, FALSE othewise
01545     Purpose:    -  used internally, mainly to find out if cache monitoring should
01546                         be turned off while modifying the file
01547  ********************************************************************************************/
01548  
01549  BOOL DownloadCache::IsCachePath(const TCHAR* szPath)
01550  {
01551      String_256 strPath(szPath);
01552      strPath.toLower();
01553      String_256 cachePath(m_strCachePath);
01554      cachePath.toLower();
01555      return (camStrstr(strPath, cachePath)) ? TRUE : FALSE;
01556  }
01557 
01558  
01559  
01560  /********************************************************************************************
01561 > void DownloadCache::InsertFile(const TCHAR* szPath)
01562 
01563     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01564     Created:    17/06/97
01565     Inputs:       - szPath - full path of file being added
01566     Returns:    - 
01567     Purpose:    - notifies the cache that a new file has been added to it. If the 
01568                         cache would overflow after the insertion, old files will be discarded
01569                         until the cache size is below the preset limit
01570  ********************************************************************************************/
01571 
01572  void DownloadCache::InsertFile(const TCHAR* szPath)
01573  {
01574     // make sure the path points to a file in the cache - if it doesn't we'll just return
01575     if (!IsCachePath(szPath))
01576         return;
01577      CacheEntry entry(szPath);
01578     if (!entry.IsValid())
01579     {
01580 #ifdef _DEBUG
01581         TCHAR szMsg[256];
01582         TCHAR szError[128];
01583         switch (errno)
01584         {
01585             case ENOENT:
01586                 camStrcpy(szError, "path not found (ENOENT)");
01587                 break;
01588             default:
01589                 wsprintf(szError, "errno = %d", errno);
01590         }
01591         wsprintf(szMsg, "Invalid cache entry: %s, %s", szPath, szError);
01592         ERROR3(szMsg);
01593 #endif
01594         return;
01595     }
01596     if (!entry.IsFile())
01597         return;
01598     TRACEUSER( "adrian", _T("Cache size before inserting %s is %u bytes\n"), entry.szFilePath, m_lCurrentSize);
01599     ::EnterCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01600     m_CacheData.push(entry);
01601     m_lCurrentSize += entry.Size();
01602     TRACEUSER( "adrian", _T("Cache size after inserting %s is %u bytes\n"), entry.szFilePath, m_lCurrentSize);
01603     TRACEUSER( "adrian", _T("Cache usage is %d percent\n"), GetUsage());
01604      // if the maximum cache size has been exceeded, discard files until we are within the limit again
01605     while (m_lCurrentSize > m_lMaxSize && m_CacheData.size() && entry != m_CacheData.top()) 
01606     {
01607         m_CacheMonitor.IgnoreEvents(1);
01608         if (!_tremove(m_CacheData.top().szFilePath))
01609         {
01610             m_lCurrentSize -= m_CacheData.top().Size();
01611             TRACEUSER( "adrian", _T("Discarded %s - new cache size is %u bytes\n"), m_CacheData.top().szFilePath, m_lCurrentSize);
01612             TRACEUSER( "adrian", _T("New cache usage is %d percent\n"), GetUsage());
01613         }
01614         else
01615             m_CacheMonitor.IgnoreEvents(-1);
01616         m_CacheData.pop();
01617     }
01618 #ifdef _DEBUG
01619     AssertCacheDataValid();
01620 #endif
01621     ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01622 }
01623 
01624 
01625 /********************************************************************************************
01626 > INT32 DownloadCache::RemoveFile(const TCHAR* szPath)
01627 
01628     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01629     Created:    17/06/97
01630     Inputs:       - strPath - full path of file being removed
01631     Returns:    - 0 if the file is successfully deleted; otherwise, it returns 1 and sets 
01632                     errno either to EACCES to indicate that the path specifies a read-only file,
01633                     or to ENOENT to indicate that the filename or path was not found or that the
01634                     path specifies a directory.
01635 
01636     Purpose:    - removes a file from the cache. Files in the cache should only be
01637                         deleted via this function
01638  ********************************************************************************************/
01639 
01640  INT32 DownloadCache::RemoveFile(const TCHAR* szPath)
01641  {
01642     ::EnterCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01643     __try
01644     {
01645          // make sure the path points to a file in the cache - if it doesn't we'll just return _tremove
01646         if (!IsCachePath(szPath))
01647              return _tremove(szPath);
01648         CacheEntry entry(szPath);
01649         INT32 nRet;
01650         if (!entry.IsValid())
01651         {
01652             if (errno != ENOENT)
01653             {
01654                 ERROR3("Invalid cache entry");
01655                 m_CacheMonitor.IgnoreEvents(1);
01656                 nRet = _tremove(szPath);
01657                 if (nRet)
01658                     m_CacheMonitor.IgnoreEvents(-1);
01659             }
01660             else 
01661                 nRet = -1;
01662             return nRet;
01663 
01664         }
01665         if (!entry.IsFile())
01666         {
01667             errno = ENOENT;
01668              return -1;
01669         }
01670         m_CacheMonitor.IgnoreEvents(1);
01671         nRet =_tremove(szPath);
01672         if (!nRet)
01673         {
01674             RemoveEntry(entry);
01675             m_lCurrentSize -= entry.Size();
01676         }
01677         else
01678         {
01679             m_CacheMonitor.IgnoreEvents(-1);    
01680     #ifdef _DEBUG
01681             TCHAR szMsg[256];
01682             TCHAR szError[128];
01683             switch (errno)
01684             {
01685                 case EACCES:
01686                     camStrcpy(szError, "access denied (EACCES)");
01687                     break;
01688                 case ENOENT:
01689                     camStrcpy(szError, "path not found (ENOENT)");
01690                     break;
01691                 default:
01692                     wsprintf(szError, "errno = %d", errno);
01693             }
01694             wsprintf(szMsg, "Failed to delete %s , %s", szPath, szError);
01695             ERROR3(szMsg);
01696     #endif
01697         }
01698     #ifdef _DEBUG
01699         AssertCacheDataValid();
01700     #endif
01701         return nRet;
01702     }
01703     __finally
01704     {
01705         ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01706     }
01707 
01708     return 0;
01709 }
01710 
01711 
01712 /********************************************************************************************
01713 > INT32 DownloadCache::RenameFile(const TCHAR* szOldName, const TCHAR* szNewName)
01714 
01715     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01716     Created:    17/06/97
01717     Inputs:       - szOldName, szNewName old/new filenames (paths)
01718     Returns:    - 0 if it is successful. On an error, the function  returns a nonzero value and sets errno to
01719                     one of the following values:
01720                     EACCES :  File or directory specified by newname already exists or could not be created
01721                     (invalid path); or strOldName is a directory and strNewName specifies a different path.
01722                     ENOENT :  File or path specified by oldname not found.
01723     Purpose:    - renames a file in the cache. Files in the cache should only be
01724                         renamed via this function
01725  ********************************************************************************************/
01726 
01727  INT32 DownloadCache::RenameFile(const TCHAR* szOldName, const TCHAR* szNewName)
01728  {
01729     String_256 strOldName(szOldName), strNewName(szNewName);
01730     BOOL bIsCachePath = IsCachePath(szOldName);
01731     if (bIsCachePath)
01732         m_CacheMonitor.IgnoreEvents(1);
01733     INT32 nRetValue = _trename(strOldName, strNewName);
01734     if (nRetValue && bIsCachePath)
01735         m_CacheMonitor.IgnoreEvents(-1);
01736      // make sure the path points to a file in the cache - if it doesn't we'll just return nRetValue
01737     if (!bIsCachePath)
01738          return nRetValue;
01739     ::EnterCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01740     if (!nRetValue)
01741     {   
01742         CacheEntry entry(strNewName);
01743         if (!entry.IsValid())
01744         {
01745             ERROR3("Invalid cache entry");
01746             ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01747             return nRetValue;
01748         }
01749         if (!entry.IsFile())
01750         {
01751             ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01752             return nRetValue;
01753         }
01754         m_CacheData.push(entry); // push the new file entry on the cache data stack
01755         camStrcpy(entry.szFilePath, strOldName); 
01756         RemoveEntry(entry); // remove old entry
01757     }
01758 #ifdef _DEBUG
01759     else
01760     {
01761         TCHAR szMsg[256];
01762         TCHAR szError[128];
01763         switch (errno)
01764         {
01765             case EACCES:
01766                 camStrcpy(szError, "file already exists or could not be created (EACCES)");
01767                 break;
01768             case ENOENT:
01769                 camStrcpy(szError, "path not found (ENOENT)");
01770                 break;
01771             default:
01772                 wsprintf(szError, "errno = %d", errno);
01773         }
01774         wsprintf(szMsg, "Failed to rename %s to %s, %s", strOldName, strNewName, szError);
01775         ERROR3(szMsg);
01776     }
01777     AssertCacheDataValid();
01778 #endif
01779     ::LeaveCriticalSection(&m_CacheMonitor.m_CacheDataCriticalSection);
01780     return nRetValue;
01781 }
01782 
01783 
01784 /********************************************************************************************
01785 > DownloadCache::CacheMonitor::CacheMonitor()
01786 
01787     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01788     Created:    17/06/97
01789     Inputs:       - 
01790     Returns:    - 
01791     Purpose:    - constructor
01792  ********************************************************************************************/
01793 
01794  DownloadCache::CacheMonitor::CacheMonitor()
01795  {
01796      m_hMonitorThread = NULL;
01797      m_dwMonitorThreadID = 0;
01798      ::InitializeCriticalSection(&m_CacheDataCriticalSection);
01799  }
01800 
01801 
01802 /********************************************************************************************
01803 > DownloadCache::CacheMonitor::~CacheMonitor()
01804 
01805     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01806     Created:    17/06/97
01807     Inputs:       - 
01808     Returns:    - 
01809     Purpose:    - destructor
01810  ********************************************************************************************/
01811 
01812  DownloadCache::CacheMonitor::~CacheMonitor()
01813  {
01814      if (m_hMonitorThread && m_dwMonitorThreadID)
01815          Deactivate();
01816      ::DeleteCriticalSection(&m_CacheDataCriticalSection);
01817  }
01818 
01819 
01820 
01821 /********************************************************************************************
01822 > BOOL DownloadCache::CacheMonitor::Activate(String_256& strCachePath)
01823 
01824     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01825     Created:    17/06/97
01826     Inputs:       - strCachePath - the cache path to be monitored
01827     Returns:    - TRUE if succesful, FALSE otherwise
01828     Purpose:    - start up the cache monitor
01829  ********************************************************************************************/
01830 
01831 BOOL DownloadCache::CacheMonitor::Activate(String_256& strCachePath)
01832 {
01833     if (rgEvents[EVENT_DEACTIVATE] || rgEvents[EVENT_CHANGE])
01834         return FALSE;
01835     if (!(rgEvents[EVENT_DEACTIVATE] = ::CreateEvent(NULL, TRUE, FALSE, NULL)))
01836         return FALSE;
01837     if ((rgEvents[EVENT_CHANGE] = ::FindFirstChangeNotification(strCachePath, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME)) == INVALID_HANDLE_VALUE)
01838         return FALSE;
01839     if (!(m_hMonitorThread = (HANDLE) ::_beginthreadex(NULL, 0, &Monitor, (LPVOID) rgEvents, 0, &m_dwMonitorThreadID)))
01840         return FALSE;
01841     return TRUE;
01842 }
01843 
01844 
01845 /********************************************************************************************
01846 > unsigned __stdcall DownloadCache::CacheMonitor::Monitor(LPVOID pParam)
01847 
01848     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01849     Created:    17/06/97
01850     Inputs:       - pParam - pointer to an array of event handles
01851     Returns:    - 0 if succesful, 0xFFFFFFFF otherwise
01852     Purpose:    - monitor thread function
01853  ********************************************************************************************/
01854 
01855 unsigned __stdcall DownloadCache::CacheMonitor::Monitor(LPVOID pParam)
01856 {
01857     TRACEUSER( "adrian", _T("Entering monitor thread\n"));
01858     HANDLE* lpEvents = (HANDLE*) pParam;
01859     while (::WaitForMultipleObjects(2, lpEvents, FALSE, INFINITE) != WAIT_OBJECT_0)
01860     {
01861         TRACEUSER( "adrian", _T("\n\n\nCache event detected\n"));
01862         if (m_nIgnoreCount > 0)
01863         {
01864             m_nIgnoreCount--;
01865 #ifdef _DEBUG
01866             TCHAR szMsg[96];
01867             wsprintf(szMsg, "Cache ignore count decremented to %d\n\n\n", m_nIgnoreCount);
01868             TRACEUSER("adrian", szMsg);
01869 #endif
01870         }
01871         else
01872             OnCacheEvent();
01873         if (!::FindNextChangeNotification(*(lpEvents + 1)))
01874             ::_endthreadex(0xFFFFFFFF);
01875     }
01876     TRACEUSER( "adrian", _T("Exiting monitor thread\n"));
01877     return 0;
01878 }
01879 
01880 
01881 /********************************************************************************************
01882 > BOOL DownloadCache::CacheMonitor::Deactivate()
01883 
01884     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01885     Created:    17/06/97
01886     Inputs:       -
01887     Returns:    - TRUE if succesful, FALSE otherwise
01888     Purpose:    - cache monitor close-down function - should be called on exit
01889  ********************************************************************************************/
01890 
01891 BOOL DownloadCache::CacheMonitor::Deactivate()
01892 {
01893     BOOL bRet = TRUE;
01894     if (!m_hMonitorThread)
01895         return bRet;
01896     if (!::SetEvent(rgEvents[0]))
01897         goto TERMINATE;
01898     if (::WaitForSingleObject(m_hMonitorThread, 1000) == WAIT_TIMEOUT)
01899     {
01900 TERMINATE:
01901         bRet = FALSE;
01902         ERROR3("Warning: monitor thread not released - will attempt to terminate it");
01903         if (!::TerminateThread(m_hMonitorThread, 0xFFFFFFFF))
01904         {
01905             TCHAR szErrorMsg[96];
01906             wsprintf(szErrorMsg, "Cannot kill zombie thread, thread ID = %lx", m_dwMonitorThreadID);
01907             ERROR3(szErrorMsg);
01908         }
01909     }
01910     ::CloseHandle(m_hMonitorThread);
01911     m_hMonitorThread = NULL;
01912     m_dwMonitorThreadID = 0;
01913     ::CloseHandle(rgEvents[0]);
01914     ::FindCloseChangeNotification(rgEvents[1]);
01915     memset(rgEvents, 0x00, sizeof(rgEvents));
01916     return bRet;
01917 }
01918 
01919 
01920 /********************************************************************************************
01921 > BOOL DownloadCache::CacheMonitor::OnCacheEvent()
01922 
01923     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01924     Created:    17/06/97
01925     Inputs:       - 
01926     Returns:    - 
01927     Purpose:    - very simple cache event-handling function. It will only output
01928                         a warning message and update the cache data
01929  ********************************************************************************************/
01930 
01931 void DownloadCache::CacheMonitor::OnCacheEvent()
01932 {
01933     String_256 strErrorMsg(_R(IDS_CACHEMODIFIED));
01934     String_256 strCaption(_R(IDS_ERRORBOX_WARNING));
01935     ::MessageBox(NULL, strErrorMsg, strCaption, MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_OK);
01936     ::EnterCriticalSection(&m_CacheDataCriticalSection);
01937     DownloadCache::Refresh();
01938     ::LeaveCriticalSection(&m_CacheDataCriticalSection);
01939 }
01940 
01941 
01942 
01943 /********************************************************************************************
01944 > CacheEntry::CacheEntry(const String_256& strPath)
01945 
01946     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
01947     Created:    17/06/97
01948     Inputs:       - path of file that that the object represents
01949     Returns:    - 
01950     Purpose:    - constructor
01951 ********************************************************************************************/
01952 CacheEntry::CacheEntry(const String_256& strPath)
01953 {
01954     camStrcpy(szFilePath, strPath);
01955     if (m_bIsValid = (!_tstat(szFilePath, &m_FileStat))) 
01956     {
01957         String_256 strFilePath(strPath);
01958         strFilePath.toLower();
01959         PathName filePath(strFilePath);
01960         if (filePath.GetType() == _T("txt"))
01961             m_nPriority = PRIORITY_CURRENT_CATALOG_FILE;
01962         else if (filePath.GetType() == _T("old"))
01963             m_nPriority = PRIORITY_OLD_CATALOG_FILE;
01964         else
01965         {
01966             //find out if the file is in a clipart, font or fill folder
01967             INT32 rgTypes[5] = {SGLib_ClipArt, SGLib_Font, SGLib_Fractal, SGLib_ClipArt_WebThemes, SGLib_Blank};
01968             INT32 nLibType;
01969             String_256 strAppDataPath;
01970             GetAppDataPath(&strAppDataPath);
01971             PathName appDataPath(strAppDataPath);
01972             for (INT32 i = 0; i < 5; i++)
01973             {
01974                 nLibType = rgTypes[i];
01975                 if (nLibType == SGLib_Blank)
01976                     break;
01977                 String_256 strCacheSubdir = GetStringField(rgTypes[i], _R(IDS_CACHEDIR));
01978                 String_256 strTypePath(appDataPath.GetLocation(TRUE)); // path of files of type rgTypes[i]
01979                 strTypePath += strCacheSubdir;
01980                 strTypePath.toLower();
01981                 if (camStrstr(strFilePath, strTypePath))
01982                     break;
01983             }
01984             // is the file a thumbnail?
01985             BOOL bIsThumb = (camStrstr(strFilePath, _T("xarainfo"))) && (filePath.GetType() == _T("png"));
01986             switch (nLibType)
01987             {
01988                 case SGLib_ClipArt:
01989                 case SGLib_ClipArt_WebThemes:
01990                     m_nPriority = bIsThumb ? PRIORITY_CLIPART_THUMBNAIL : PRIORITY_CLIPART_FILE;
01991                     break;
01992                 case SGLib_Font:
01993                     m_nPriority = bIsThumb ? PRIORITY_FONT_THUMBNAIL : PRIORITY_FONT_FILE;
01994                     break;
01995                 case SGLib_Fractal:
01996                     m_nPriority = bIsThumb ? PRIORITY_FILL_THUMBNAIL : PRIORITY_FILL_FILE;
01997                     break;
01998                 default:
01999                     m_nPriority = PRIORITY_OTHER;
02000             }
02001         }
02002     }
02003 }
02004 
02005 
02006 
02008 // Internet Manager
02010 
02011 // Static members
02012 INT32 InternetManager::nMaxConnections = 32; // maximum no. of simultaneous connections/downloads
02013 CTypedPtrArray<CPtrArray, AsynchDownload*> InternetManager::m_CurrentDownloads;
02014 DownloadQueue InternetManager::m_NormalPriorityQueue;
02015 DownloadQueue InternetManager::m_HighPriorityQueue;
02016 DOWNLOAD_HANDLE InternetManager::m_NextHandle = 1L;
02017 AsynchDownload::State InternetManager::m_StateTable[1000];
02018 String_256 InternetManager::strTempFileURL; 
02019 BOOL InternetManager::m_bIsSuspended = FALSE;
02020 INT32 InternetManager::rgConnections[4] = {4, 8, 20, 64};
02021 
02022 // Preferences
02023 UINT32  InternetManager::g_ConnectionType       = CONNECTION_SLOWMODEM; // we default to the most prudent setting
02024 UINT32  InternetManager::g_CacheSize            = 5000;
02025 
02026 
02027 /********************************************************************************************
02028 > InternetManager::Initialize
02029 
02030     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02031     Created:    18/11/96
02032     Inputs:       -
02033     Returns:    - 
02034     Purpose:    - initialization function
02035 ********************************************************************************************/
02036 void InternetManager::Initialize()
02037 {
02038     WORD wVersionRequested; // first initialize to socket library
02039     WSADATA wsaData; 
02040     wVersionRequested = MAKEWORD(1, 1); 
02041     if (::WSAStartup(wVersionRequested, &wsaData))
02042         InformError(_R(IDS_SOCKETNOTINITIALIZED));
02043     m_HighPriorityQueue.SetType(DownloadQueue::LIFO); // the high priority queue is LIFO by default
02044     String_256 strCachePath;
02045     GetAppCachePath(&strCachePath);
02046     DownloadCache::SetPath(strCachePath);
02047     /*if (!DownloadCache::m_CacheMonitor.Activate(strCachePath))
02048     {
02049         ERROR3("Cannot activate the cache monitor");
02050     }*/
02051 /*
02052     // This code is an abortion and unmaintainable. Why not use the preference system?
02053     HKEY arhKeys[6];
02054     DWORD dwConnectionType = 0L;
02055     UINT32 lCacheSize = 0L;
02056     DWORD dwSizeofDword = sizeof(DWORD), dwSizeofLong = sizeof(INT32);
02057     DWORD dwDisposition;
02058     memset(arhKeys, 0, sizeof(arhKeys));
02059     String_256 strProgramKey = GetProgramNameRegistryKey();
02060     BOOL bResult = ((RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &arhKeys[0]) == ERROR_SUCCESS) &&
02061         (RegCreateKeyEx(arhKeys[0], _tcstok(strProgramKey, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[1], &dwDisposition) == ERROR_SUCCESS) &&
02062         (RegCreateKeyEx(arhKeys[1], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[2], &dwDisposition) == ERROR_SUCCESS) &&
02063         (RegCreateKeyEx(arhKeys[2], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[3], &dwDisposition) == ERROR_SUCCESS) &&
02064         (RegCreateKeyEx(arhKeys[3], _T("Options"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[4], &dwDisposition) == ERROR_SUCCESS) &&
02065         (RegCreateKeyEx(arhKeys[4], _T("Internet"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[5], &dwDisposition) == ERROR_SUCCESS) &&
02066         (RegQueryValueEx(arhKeys[5], _T("Connection Type"), NULL, NULL, (LPBYTE) &dwConnectionType, &dwSizeofDword) == ERROR_SUCCESS) && 
02067         (RegQueryValueEx(arhKeys[5], _T("Cache Size"),  NULL, NULL, (LPBYTE) &lCacheSize, &dwSizeofLong) == ERROR_SUCCESS));
02068     for (INT32 i = 5; i >= 0; i--) RegCloseKey(arhKeys[i]);
02069     if (bResult)
02070     {
02071         nMaxConnections = rgConnections[dwConnectionType];
02072         DownloadCache::SetSize(lCacheSize);
02073     }
02074     else
02075         SetConnectionType(CONNECTION_SLOWMODEM); // we default to the most prudent setting 
02076 */
02077 
02078     if (Camelot.DeclareSection(TEXT("Internet"), 4))
02079     {
02080         Camelot.DeclarePref(NULL, TEXT("Connection Type"), &g_ConnectionType, 0, 3);
02081         Camelot.DeclarePref(NULL, TEXT("Cache Size"), &g_CacheSize, 64 * SIZEOFKILOBYTE);
02082     }
02083     nMaxConnections = rgConnections[g_ConnectionType];
02084     DownloadCache::SetSize(g_CacheSize);
02085 }
02086 
02087 
02088 /********************************************************************************************
02089 > InternetManager::OnExitInstance()
02090 
02091     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02092     Created:    18/11/96
02093     Inputs:       -
02094     Returns:    -
02095     Purpose:    - close-down function
02096 ********************************************************************************************/
02097 
02098 
02099 void InternetManager::OnExitInstance()
02100 {
02101     ::WSACleanup();
02102     /*if (!DownloadCache::m_CacheMonitor.Deactivate())
02103     {
02104         ERROR3("Cannot deactivate the cache monitor - likely to leak on exit");
02105     }*/
02106 
02107     // ensure there are no downloads left behind
02108     if (HasDownloadsPending())
02109     {
02110         m_HighPriorityQueue.Flush();
02111         m_NormalPriorityQueue.Flush();
02112         for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02113         {
02114             if (m_CurrentDownloads[i])
02115                 m_CurrentDownloads[i]->Release();
02116         }
02117     }
02118     // Remove the last downloaded file from the cache, if any
02119     if (!strTempFileURL.IsEmpty())
02120     {
02121         UnlockUrlCacheEntryFile((TCHAR*) strTempFileURL, 0);
02122         DeleteUrlCacheEntry((TCHAR*) strTempFileURL); // nb: this call may fail if the file is still locked for some reason
02123     }
02124 }
02125 
02126 
02127 
02128 
02129 /********************************************************************************************
02130 > DOWNLOAD_HANDLE InternetManager::RegisterDownload(LPDOWNLOADINFO pDownloadInfo)
02131 
02132     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02133     Created:    18/11/96
02134     Inputs:      pDownloadInfo - pointer to DOWNLOADINFO struct
02135     Returns:    non-zero download handle if successful, 0 otherwise
02136     Purpose:    register a new file download. The download will be started immediately if nothing else is pending; otherwise, it will just be 
02137                     queued up
02138     NOTES    1. Client objects are not directly notified of the outcome of the downloads they have registered. Callback functions called
02139                     through pointers are not a very safe way to go as the pointer might not be valid anymore by the time the download is over.
02140                     Instead, the client should poll the InternetManager for the result of the download, using the handle return by RegisterDownload().
02141                     Usually the client object will be derived from Operation so the polling could be done on IdleEvent() .
02142                     2. If the queue with the given priority is locked, the download is registered and you get a valid handle but it will immediatelly
02143                     flagged as ABORTED. The queues are locked in response to a user cancel. 
02144 
02145     SUBSEQUENT NOTES by Phil:
02146     04/05/2004  Client objects CAN now be notified of the outcome of the downloads. (Idle
02147                 polling of the download status was thought to be too tacky.)
02148                 RegisterDownload now takes in a window handle parameter and  token parameter.
02149                 If the handle is valid and the token is non-zero then custom
02150                 messages will be sent to that window containing the token. The handler of
02151                 those messages can use the token to decide which object should receive the
02152                 notification.
02153 
02154 ********************************************************************************************/
02155 
02156 
02157 
02158 DOWNLOAD_HANDLE InternetManager::RegisterDownload(LPDOWNLOADINFO pDownloadInfo)
02159 {
02160     if (m_NextHandle == 1L) // first download, initialise the state table
02161         memset(m_StateTable, 0, sizeof(m_StateTable));
02162 
02163     // Check if the download has been previously registered - if so, return the same handle
02164     AsynchDownload* pPreviousDownload = GetPendingDownload(pDownloadInfo->strLocalFile);
02165     if (pPreviousDownload)
02166         return pPreviousDownload->GetHandle();
02167 
02168     DOWNLOAD_HANDLE handle = m_NextHandle;
02169     m_NextHandle++;
02170     if (camStrstr((TCHAR*) pDownloadInfo->strURL, _T("file://")))
02171     {
02172         PathNameEx(pDownloadInfo->strLocalFile).CreateLocation();
02173 #ifdef UNICODE
02174         WCHAR wchURL[INTERNET_MAX_PATH_LENGTH];
02175         MultiByteToWideChar(CP_ACP, 0,  (TCHAR*) pDownloadInfo->strURL, -1, wchURL, INTERNET_MAX_PATH_LENGTH);
02176         if (SUCCEEDED(::URLDownloadToFile(NULL, wchURL, (TCHAR*) pDownloadInfo->strLocalFile, 0, NULL)))
02177 #else
02178         if (SUCCEEDED(::URLDownloadToFile(NULL, (TCHAR*) pDownloadInfo->strURL, (TCHAR*) pDownloadInfo->strLocalFile, 0, NULL)))
02179 #endif
02180 //          m_StateTable[handle] = AsynchDownload::STATE_SUCCEEDED;
02181             SetState(handle, AsynchDownload::STATE_SUCCEEDED);
02182         else
02183 //          m_StateTable[handle] = AsynchDownload::STATE_FAILED;
02184             SetState(handle, AsynchDownload::STATE_FAILED);
02185         return handle;
02186     }
02187     AsynchDownload* pDownload = new AsynchDownload(handle, pDownloadInfo);
02188     if (!pDownload)
02189     {
02190         ERROR3("Memory allocation error");
02191         return (DOWNLOAD_HANDLE) INVALID_HANDLE_VALUE;
02192     }
02193 //  m_StateTable[handle] = AsynchDownload::STATE_PENDING;
02194     SetState(handle, AsynchDownload::STATE_PENDING);
02195     if (m_CurrentDownloads.GetSize() < nMaxConnections && !m_bIsSuspended) // can start it straight away
02196     {
02197         if (pDownloadInfo->nPriority == AsynchDownload::PRIORITY_NORMAL && GetOpenConnections(AsynchDownload::PRIORITY_NORMAL))
02198             goto QUEUE; // this is a normal pDownloadInfo->nPriority download and we already have one executing, so we just queue it up
02199         if (!SUCCEEDED(pDownload->StartDownload()))
02200         {
02201 //          m_StateTable[handle] = AsynchDownload::STATE_FAILED;
02202             SetState(handle, AsynchDownload::STATE_FAILED);
02203             pDownload->Release();
02204         }
02205         else
02206         {
02207             // add it to the array of executing downloads
02208             m_CurrentDownloads.Add(pDownload);
02209             if (pDownloadInfo->nPriority == AsynchDownload::PRIORITY_NORMAL)
02210             TRACEUSER( "adrian", _T("Download fired up for %s\n"), (TCHAR*) String_256(pDownload->GetLocalFileName())); 
02211         }
02212     }
02213     else
02214     {
02215 QUEUE:
02216         // queue the download, as we shouldn't exceed the allowed number of connections
02217         if (pDownloadInfo->nPriority == AsynchDownload::PRIORITY_NORMAL)
02218             m_NormalPriorityQueue.Queue(pDownload);
02219         else
02220         {
02221             // switch the high pDownloadInfo->nPriority queue to FIFO mode if this is a thumbnail
02222             if (pDownloadInfo->nFileType == TYPE_THUMBNAIL)
02223                 m_HighPriorityQueue.SetType(DownloadQueue::FIFO);
02224             m_HighPriorityQueue.Queue(pDownload);
02225             if (pDownloadInfo->nFileType == TYPE_THUMBNAIL)
02226                 m_HighPriorityQueue.SetType(DownloadQueue::LIFO); // back to default (LIFO) mode
02227         }
02228     }
02229     return handle;
02230 }
02231 
02232 
02233 
02234 /********************************************************************************************
02235 > BOOL InternetManager::UnregisterDownload(DOWNLOAD_HANDLE hDownload)
02236 
02237     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02238     Created:    18/11/96
02239     Inputs:       hDownload - handle of download as returned by a previous call to RegisterDownload()
02240     Returns:    TRUE if succeeds, FALSE
02241     Purpose:    unregisters a previously registered download. It behaves like the following:
02242                             - if the download has already finished, the function will fail
02243                             - if the download is in progress, it will be aborted on the next call from the callback object,
02244                                 and the AsynchDownload object deleted as soon as this call returns
02245                             - if the download is still queued up, it will be removed from the queue and deleted
02246 ********************************************************************************************/
02247 
02248 
02249 
02250 BOOL InternetManager::UnregisterDownload(DOWNLOAD_HANDLE hDownload)
02251 {
02252     AsynchDownload::State downloadState = GetDownloadState(hDownload);
02253     if (downloadState == AsynchDownload::STATE_PENDING) // download is still pending, i.e. either happening now or queued up
02254     {
02255         AsynchDownload* pDownload = GetPendingDownload(hDownload);
02256         if (!pDownload)
02257         {
02258             ERROR3("Unexpected NULL pointer");
02259             return FALSE;
02260         }
02261         // We flag the download as aborted, although we may not be able to abort it immediately - it might even succeed in the mean time
02262         m_StateTable[pDownload->GetHandle()] = AsynchDownload::STATE_ABORTED; 
02263 // Call SetState???
02264         // Find out if the download is actually executing
02265         BOOL bIsExecuting = FALSE;
02266         for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02267         {
02268             if (m_CurrentDownloads[i] == pDownload)
02269                 bIsExecuting = TRUE;
02270         }
02271         if (bIsExecuting) // download happening now
02272         {
02273             // Abort the download. Note that we don't delete it, doing so would result in a leak of socket resources.
02274             // AbortDownload() is asynchronous and returns immediately - the download will actually be aborted
02275             // when the server acknowedges the TCP_RESET message and closes the connection so Winsock.dll
02276             // can close its open socket.
02277             HRESULT result = pDownload->AbortDownload();
02278             ERROR3IF(result == E_FAIL, "Could not abort download");
02279         }
02280         else // download is still in one of the queues
02281         {
02282             if (m_NormalPriorityQueue.Remove(pDownload) || m_HighPriorityQueue.Remove(pDownload))
02283                 return TRUE;
02284             else
02285             {
02286                 ERROR3("Failed to unregister download");
02287                 return FALSE;
02288             }
02289         }
02290     }
02291     return TRUE;
02292 }
02293 
02294 
02295 
02296 
02297 /********************************************************************************************
02298 > AsynchDownload InternetManager::GetPendingDownload(const String_256& strFileName)
02299 
02300     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02301     Created:    18/11/96
02302     Inputs:       strFileName
02303     Returns:    pointer to AsynchDownload object if found, NULL otherwise
02304     Purpose:    get a pointer to a download object knowing the the local file it's downloading to
02305 ********************************************************************************************/
02306 
02307 
02308 AsynchDownload* InternetManager::GetPendingDownload(const String_256& strFileName)
02309 {
02310     AsynchDownload* pDownload = NULL;
02311     String_256 strLocalFileName(strFileName);
02312     // Check the downloads currently executing
02313     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02314         if (!strLocalFileName.CompareTo(m_CurrentDownloads[i]->GetLocalFileName(), FALSE))
02315             return m_CurrentDownloads[i];
02316     // Check the download queues
02317     if (pDownload = m_NormalPriorityQueue.FindDownload(strLocalFileName))
02318         return pDownload;
02319     pDownload = m_HighPriorityQueue.FindDownload(strLocalFileName);
02320     return pDownload;
02321 }
02322 
02323 
02324 
02325 /********************************************************************************************
02326 > INT32 InternetManager::GetPercentageDownloaded(DOWNLOAD_HANDLE hDownload)
02327 
02328     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02329     Created:    18/11/96
02330     Inputs:       hDownload
02331     Returns:    an INT32 in the range of 0-100 if the handle is valid, -1 otherwise
02332     Purpose:    monitor the percentage downloaded on a handle
02333 ********************************************************************************************/
02334 
02335 INT32 InternetManager::GetPercentageDownloaded(DOWNLOAD_HANDLE hDownload)
02336 {
02337     AsynchDownload* pDownload = GetPendingDownload(hDownload);
02338     if (pDownload)
02339         return pDownload->GetPercentageDownloaded();
02340     else
02341         return -1;
02342 }
02343 
02344 
02345 
02346 /********************************************************************************************
02347 > AsynchDownload InternetManager::GetPendingDownload(DOWNLOAD_HANDLE hDownload)
02348 
02349     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02350     Created:    18/11/96
02351     Inputs:       hDownload
02352     Returns:    pointer to AsynchDownload object if found, NULL otherwise
02353     Purpose:    get a pointer to a download object knowing the download's handle
02354 ********************************************************************************************/
02355 
02356 AsynchDownload* InternetManager::GetPendingDownload(DOWNLOAD_HANDLE hDownload)
02357 {
02358     AsynchDownload* pDownload = NULL;
02359     AsynchDownload::State downloadState = GetDownloadState(hDownload);
02360     if (downloadState != AsynchDownload::STATE_PENDING)
02361         return pDownload;
02362     // Check the downloads currently executing
02363     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02364     {
02365         if (m_CurrentDownloads[i]->GetHandle() == hDownload)
02366             return m_CurrentDownloads[i];
02367     }
02368     // Check the download queues
02369     if (pDownload = m_NormalPriorityQueue.FindDownload(hDownload))
02370         return pDownload;
02371     pDownload = m_HighPriorityQueue.FindDownload(hDownload);
02372     return pDownload;
02373 }
02374 
02375 
02376 
02377 /********************************************************************************************
02378 > AsynchDownload InternetManager::GetDownload(DOWNLOAD_HANDLE hDownload)
02379 
02380     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02381     Created:    04/05/2004
02382     Inputs:       hDownload
02383     Returns:    pointer to AsynchDownload object if found, NULL otherwise
02384     Purpose:    get a pointer to a download object knowing the download's handle
02385 ********************************************************************************************/
02386 
02387 AsynchDownload* InternetManager::GetDownload(DOWNLOAD_HANDLE hDownload)
02388 {
02389     AsynchDownload* pDownload = NULL;
02390 
02391     // Check the downloads currently executing
02392     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02393     {
02394         if (m_CurrentDownloads[i]->GetHandle() == hDownload)
02395             return m_CurrentDownloads[i];
02396     }
02397     // Check the download queues
02398     if (pDownload = m_NormalPriorityQueue.FindDownload(hDownload))
02399         return pDownload;
02400     pDownload = m_HighPriorityQueue.FindDownload(hDownload);
02401     return pDownload;
02402 }
02403 
02404 
02405 
02406 /********************************************************************************************
02407 > AsynchDownload:State InternetManager::GetDownloadState(DOWNLOAD_HANDLE hDownload)
02408 
02409     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02410     Created:    18/11/96
02411     Inputs:       hDownload - handle of the download inquired on
02412     Returns:    a value of InternetManager::State type reflecting the state of the download:
02413                     STATE_ERROR - the hDownload param is not valid or some other internal error occurred
02414                     STATE_PENDING - download is still pending 
02415                     STATE_FAILED - download has failed through network or other "natural" errors
02416                     STATE_SUCCEEDED - download has succeeded 
02417                     STATE_ABORTED - user cancel
02418     Purpose:   Test the current state of a registered download - usually used in a loop polling for
02419                     completion, either successful or failed
02420 ********************************************************************************************/
02421 
02422 
02423 AsynchDownload::State InternetManager::GetDownloadState(DOWNLOAD_HANDLE hDownload)
02424 {
02425     // For quick access handles are used as indexes into the state table
02426     if ((hDownload < 1) || (hDownload >= MAX_HANDLE))
02427         return AsynchDownload::STATE_ERROR;
02428     else
02429         return m_StateTable[hDownload];
02430 }
02431 
02432 
02433 /********************************************************************************************
02434 > void InternetManager::OnDownloadComplete(AsynchDownload* pDownload)
02435 
02436     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02437     Created:    18/11/96
02438     Inputs:       pDownload - pointer to object sending the notification
02439     Returns:    none 
02440     Purpose:   Callback function notifying the download manager the an object has completed downloading
02441                     Used internally only.
02442 ********************************************************************************************/
02443 
02444 
02445 
02446 void InternetManager::OnDownloadComplete(AsynchDownload* pDownload)
02447 {
02448     if (!pDownload)
02449     {
02450         ERROR3("Unexpected NULL pointer");
02451         return;
02452     }
02453     if (pDownload->HasSucceeded())
02454     {
02455 //      m_StateTable[pDownload->GetHandle()] = AsynchDownload::STATE_SUCCEEDED;
02456         DownloadCache::InsertFile(pDownload->GetLocalFileName());
02457         SetState(pDownload->GetHandle(), AsynchDownload::STATE_SUCCEEDED);
02458     }
02459     else
02460     {
02461         if (pDownload->WasAborted()) // user cancel
02462 //          m_StateTable[pDownload->GetHandle()] = AsynchDownload::STATE_ABORTED;   
02463             SetState(pDownload->GetHandle(), AsynchDownload::STATE_ABORTED);
02464         else // failed through network or other errors
02465 //          m_StateTable[pDownload->GetHandle()] = AsynchDownload::STATE_FAILED;
02466             SetState(pDownload->GetHandle(), AsynchDownload::STATE_FAILED);
02467     }
02468     
02469     // Remove the download from its slot 
02470     INT32 nSlot = -1;
02471     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02472     {
02473         if (m_CurrentDownloads[i] == pDownload)
02474             nSlot = i;
02475     }
02476     ERROR3IF(nSlot == -1, "Download terminated without being executed");
02477     if (nSlot != -1)
02478         m_CurrentDownloads.RemoveAt(nSlot);
02479     if (!strTempFileURL.IsEmpty())
02480     {
02481         UnlockUrlCacheEntryFile((TCHAR*) strTempFileURL, 0);
02482         DeleteUrlCacheEntry((TCHAR*) strTempFileURL); // nb: this call may fail if the file is still locked for some reason
02483     }
02484     strTempFileURL = pDownload->GetTargetURL(); // URL of temp file to be removed on the next call
02485     if (nSlot != -1)
02486         pDownload->Release();
02487     if (m_bIsSuspended) // if suspended simply return
02488         return;
02489     // Start the next download in the queues, if any - we should have at least one connection available
02490     while ((!m_HighPriorityQueue.IsEmpty() || !m_NormalPriorityQueue.IsEmpty()) && m_CurrentDownloads.GetSize() < nMaxConnections)
02491     {
02492         AsynchDownload* pNextDownload = m_HighPriorityQueue.GetNextDownload();
02493         if (!pNextDownload)
02494         {
02495             // the high priority queue is empty; we'll start a normal priority one if none is already executing or the queue is not
02496             // locked
02497             if  (!GetOpenConnections(AsynchDownload::PRIORITY_NORMAL))
02498                 pNextDownload = m_NormalPriorityQueue.GetNextDownload();
02499         }
02500         if (!pNextDownload) // both queues empty or locked
02501             return;
02502         if (!SUCCEEDED(pNextDownload->StartDownload()))
02503         {
02504 //          m_StateTable[pNextDownload->GetHandle()] = AsynchDownload::STATE_FAILED;
02505             SetState(pNextDownload->GetHandle(), AsynchDownload::STATE_FAILED);
02506             pNextDownload->Release();
02507         }
02508         else
02509         {
02510             // add it the array of executing downloads
02511             m_CurrentDownloads.Add(pNextDownload);
02512             TRACEUSER( "adrian", _T("Download fired up for %s\n"), (TCHAR*) String_256(pNextDownload->GetLocalFileName())); 
02513         }
02514     }
02515 }
02516 
02517 
02518 /********************************************************************************************
02519 > void InternetManager::CancelAllDownloads()
02520 
02521     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02522     Created:    18/11/96
02523     Inputs:       none
02524     Returns:    none 
02525     Purpose:   Aborts any downloads in progress and flushes both queues. Any download
02526                     thus removed will be flagged as ABORTED.
02527                     Should be called either if the user chooses to abort all downloads or
02528                     we detect some severe network error that makes it pointless to continue.
02529 ********************************************************************************************/
02530         
02531 void InternetManager::CancelAllDownloads()
02532 {
02533     // First empty the queues
02534     while (!m_HighPriorityQueue.IsEmpty())
02535     {
02536         AsynchDownload* pDownload = m_HighPriorityQueue.GetNextDownload();
02537         if (pDownload)
02538             UnregisterDownload(pDownload->GetHandle());
02539     }
02540     while (!m_NormalPriorityQueue.IsEmpty())
02541     {
02542         AsynchDownload* pDownload = m_NormalPriorityQueue.GetNextDownload();
02543         if (pDownload)
02544             UnregisterDownload(pDownload->GetHandle());
02545     }
02546     // Abort the downloads in progress if any
02547     // Note that we don't delete it straight away as we might leek resources if we did so - instead,
02548     // the object will be deleted when AbortDownload() (which is asynchronous) completes
02549     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02550     {
02551         HRESULT result = m_CurrentDownloads[i]->AbortDownload();
02552         ERROR3IF(result == E_FAIL , "Could not abort download");
02553     }
02554 }
02555 
02556 
02557 
02558 
02559 /********************************************************************************************
02560 > INT32 InternetManager::GetOpenConnections(AsynchDownload::Priority priority)
02561 
02562     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02563     Created:    18/11/96
02564     Inputs:       priority of the connections enquired about (enumerated type in AsynchDownload)
02565     Returns:    number of connections with the given priority which are currently open
02566     Purpose:   used in scheduling downloads
02567 ********************************************************************************************/
02568 
02569 INT32 InternetManager::GetOpenConnections(AsynchDownload::Priority priority)
02570 {
02571     INT32 nDownloads = 0;
02572     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02573     {
02574         if (m_CurrentDownloads[i]->GetPriority() == priority)
02575             nDownloads++;
02576     }
02577     return nDownloads;
02578 }
02579 
02580 
02581 
02582                             
02583 
02584 /********************************************************************************************
02585 > void InternetManager::Resume()
02586 
02587     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02588     Created:    18/11/96
02589     Inputs:       none
02590     Returns:    none 
02591     Purpose:   Resumes normal downloading previously stopped by a call to Suspend()
02592 
02593 ********************************************************************************************/
02594 
02595 void InternetManager::Resume()
02596 {
02597     m_bIsSuspended = FALSE;
02598     while ((!m_HighPriorityQueue.IsEmpty() || !m_NormalPriorityQueue.IsEmpty()) && m_CurrentDownloads.GetSize() < nMaxConnections)
02599     {
02600         AsynchDownload* pNextDownload = m_HighPriorityQueue.GetNextDownload();
02601         if (!pNextDownload)
02602         {
02603             // the high priority queue is empty; we'll start a normal priority one if none is already executing or the queue is not
02604             // locked
02605             if  (!GetOpenConnections(AsynchDownload::PRIORITY_NORMAL))
02606                 pNextDownload = m_NormalPriorityQueue.GetNextDownload();
02607         }
02608         if (!pNextDownload) // both queues empty or locked
02609             return;
02610         if (!SUCCEEDED(pNextDownload->StartDownload()))
02611         {
02612 //          m_StateTable[pNextDownload->GetHandle()] = AsynchDownload::STATE_FAILED;
02613             SetState(pNextDownload->GetHandle(), AsynchDownload::STATE_FAILED);
02614             pNextDownload->Release();
02615         }
02616         else
02617         {
02618             // add it the array of executing downloads
02619             m_CurrentDownloads.Add(pNextDownload);
02620             TRACEUSER( "adrian", _T("Download fired up for %s\n"), (TCHAR*) String_256(pNextDownload->GetLocalFileName())); 
02621         }
02622     }
02623 }
02624 
02625 /********************************************************************************************
02626 > AsynchDownload* InternetManager::GetCurrentNormalPriorityDownload()
02627 
02628     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02629     Created:    18/11/96
02630     Inputs:       none
02631     Returns:    pointer to the currently executing normal priority download, NULL if none is found 
02632     Purpose:   provides access to thumbnail & catalog downloading (monitor/abort it, etc)
02633 
02634 ********************************************************************************************/
02635 
02636 AsynchDownload* InternetManager::GetCurrentNormalPriorityDownload()
02637 {
02638     AsynchDownload* pDownload = NULL;
02639     for (INT32 i = 0; i < m_CurrentDownloads.GetSize(); i++) 
02640     {
02641         if (m_CurrentDownloads[i]->GetPriority() == AsynchDownload::PRIORITY_NORMAL)
02642             pDownload = m_CurrentDownloads[i];
02643     }
02644     return pDownload;
02645 }
02646 
02647 
02648 
02649 
02650 /********************************************************************************************
02651 > void InternetManager::SetConnectionType()
02652 
02653     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02654     Created:    18/11/96
02655     Inputs:       type - one of CONNECTION_SLOWMODEM, CONNECTION_FASTMODEM, 
02656                         CONNECTION_X2, CONNECTION_ISDN 
02657     Returns:    - 
02658     Purpose:   - allows the altering of the default connection type (CONNECTION_SLOWMODEM) 
02659                         Setting this parameter accurately should result in improved download speed.
02660 
02661 ********************************************************************************************/
02662 void InternetManager::SetConnectionType(ConnectionType type)
02663 {
02664     // Save away the type in our preference variable
02665     g_ConnectionType = type;
02666     // Now set the new number of maximum connections according to this
02667     nMaxConnections = rgConnections[type];
02668 
02669     // When the user sets their coannection speed explicitly, we should also
02670     // update the flag that the helkp and support download system uses
02671     if (type==CONNECTION_ISDN)
02672         HelpDownloadOp::HighBandwidth = TRUE;
02673     else
02674         HelpDownloadOp::HighBandwidth = FALSE;
02675 
02676 /*
02677     // This code is an abortion and unmaintainable. Why not use the preference system?
02678     HKEY arhKeys[6];
02679     DWORD dwConnectionType = (DWORD) type;
02680     DWORD dwDisposition;
02681     memset(arhKeys, 0, sizeof(arhKeys));
02682     String_256 strProgramKey = GetProgramNameRegistryKey();
02683     BOOL bResult = ((RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &arhKeys[0]) == ERROR_SUCCESS) &&
02684         (RegCreateKeyEx(arhKeys[0], _tcstok(strProgramKey, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[1], &dwDisposition) == ERROR_SUCCESS) &&
02685         (RegCreateKeyEx(arhKeys[1], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[2], &dwDisposition) == ERROR_SUCCESS) &&
02686         (RegCreateKeyEx(arhKeys[2], _tcstok(NULL, _T("\\")),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[3], &dwDisposition) == ERROR_SUCCESS) &&
02687         (RegCreateKeyEx(arhKeys[3], _T("Options"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[4], &dwDisposition) == ERROR_SUCCESS) &&
02688         (RegCreateKeyEx(arhKeys[4], _T("Internet"),  0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &arhKeys[5], &dwDisposition) == ERROR_SUCCESS) &&
02689         (RegSetValueEx(arhKeys[5], _T("Connection Type"),   NULL, REG_DWORD, (LPBYTE) &dwConnectionType,    sizeof(dwConnectionType)) == ERROR_SUCCESS));
02690     for (INT32 i = 5; i >= 0; i--) RegCloseKey(arhKeys[i]);
02691     ERROR3IF(!bResult, "Failed to save changes to registry");
02692 */
02693  }
02694 
02695 
02696 /********************************************************************************************
02697 > void InternetManager::GetConnectionType()
02698 
02699     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02700     Created:    18/11/96
02701     Inputs:       -
02702     Returns:    -  one of CONNECTION_SLOWMODEM, CONNECTION_FASTMODEM, 
02703                         CONNECTION_X2, CONNECTION_ISDN
02704     Purpose:   - retrieves the connection type the InternetManager is configured for. The
02705                         connection type is inferred from the maximum number of connections 
02706                         that can be opened simultaneously
02707 
02708 ********************************************************************************************/
02709 ConnectionType InternetManager::GetConnectionType()
02710 {
02711     for (INT32 type = (INT32) CONNECTION_SLOWMODEM; type <= (INT32) CONNECTION_ISDN; type++)
02712     {
02713         if (rgConnections[type] == nMaxConnections) 
02714             break; 
02715     }
02716     return (ConnectionType) type;
02717 }
02718 
02719 /********************************************************************************************
02720 > BOOL InternetManager::GetProxyServer(SERVENT* pProxyEntry, bool* pbProxyEnabled)
02721     Author:     Adrian_Stoicar (Xara Group Ltd) <camelotdev@xara.com>
02722     Created:    18/11/96
02723     Inputs:     - pProxyEntry - pointer to SERVENT structure to receive proxy information                       
02724     Returns:    -  TRUE if successful, FALSE otherwise (in which case the struct pointed to by 
02725                 pProxyAddress will contain garbage and should not be used any further)
02726     Purpose:    - gets the current (system-wide) proxy settings
02727 ********************************************************************************************/
02728 bool InternetManager::GetProxyServer(SERVENT* pProxyEntry, bool* pbProxyEnabled)
02729 {
02730     // Get the current proxy settings
02731     TCHAR szProxy[_MAX_PATH];
02732     String_256 KeySectionName(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
02733     HKEY hKey = OpenRegKey(HKEY_CURRENT_USER, KeySectionName);
02734     if (hKey)
02735     {
02736         String_256 ProxyServer;
02737         BOOL ok = GetRegString(hKey, TEXT("ProxyServer"), &ProxyServer);
02738         if (ok)
02739             camStrcpy(szProxy, (TCHAR*)ProxyServer);
02740         *pbProxyEnabled = GetRegBool(hKey, TEXT("ProxyEnable")) == TRUE;
02741         CloseRegKey(hKey);
02742     }
02743 
02744     // If the string is blank then the proxy must be off
02745     if (!camStrlen(szProxy))
02746     {
02747         *pbProxyEnabled = false;
02748         return true;
02749     }
02750 
02751     String_256 strHost, strPort;
02752     TCHAR tchBuff[_MAX_PATH];
02753     camStrcpy(tchBuff, szProxy);
02754     TCHAR* lpszTemp = NULL;
02755     lpszTemp = camStrstr(tchBuff, _T("http="));
02756     if (lpszTemp != NULL)
02757     {
02758         strHost = _tcstok((lpszTemp + 5), _T(":"));
02759         strPort = _tcstok(NULL, _T(";"));
02760     }
02761     else
02762     {
02763         strHost = _tcstok(tchBuff, _T(":"));
02764         strPort = _tcstok(NULL, _T(";"));
02765     }
02766     if (!camStrlen(strHost) || !camStrlen(strPort))
02767     {
02768         *pbProxyEnabled = false;
02769         return true;
02770     }
02771 
02772     camStrcpy(pProxyEntry->s_name, strHost);
02773     pProxyEntry->s_port = (unsigned short) atoi(strPort);
02774     return true;
02775 }
02776 
02777 
02778 /********************************************************************************************
02779 > void InternetManager::SetState(DOWNLOAD_HANDLE hDownload, AsynchDownload::State state)
02780 
02781     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
02782     Created:    04/05/2004
02783     Inputs:     -
02784     Returns:    -
02785     Purpose:    - Set the status of this download and inform the client of the change
02786 
02787 ********************************************************************************************/
02788 void InternetManager::SetState(DOWNLOAD_HANDLE hDownload, AsynchDownload::State state)
02789 {
02790     m_StateTable[hDownload] = state;
02791 
02792     // -------------------------------------
02793     // Find the download object
02794     AsynchDownload* pDownload = GetDownload(hDownload);
02795 
02796     // -------------------------------------
02797     // If we have it then see whether the client wants to be notified
02798     if (pDownload)
02799     {
02800         HWND hwndNotify = pDownload->GetNotifyHWND();
02801         INT32 lNotifyToken = pDownload->GetNotifyToken();
02802         if (hwndNotify && lNotifyToken)
02803         {
02804             // Send the message to the client without waiting
02805             ::PostMessage(hwndNotify, WM_USER_DOWNLOADSTATUS, state, lNotifyToken);
02806         }
02807     }
02808 }
02809 

Generated on Sat Nov 10 03:48:09 2007 for Camelot by  doxygen 1.4.4