[RAPPS]
[reactos.git] / reactos / base / applications / rapps / available.cpp
index f6e12b8..f484260 100644 (file)
  * PROJECT:         ReactOS Applications Manager
  * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            base/applications/rapps/available.cpp
- * PURPOSE:         Functions for working with available applications
+ * PURPOSE:         Classes for working with available applications
  * PROGRAMMERS:     Dmitry Chapyshev           (dmitry@reactos.org)
  *                  Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
  *                  Alexander Shaposhnikov     (chaez.san@gmail.com)
  */
+#include "defines.h"
 
-#include "rapps.h"
+#include "available.h"
+#include "misc.h"
+#include "dialogs.h"
 
-ATL::CAtlList<PAPPLICATION_INFO> InfoList;
+#include <atlcoll.h>
+#include <atlsimpcoll.h>
+#include <atlstr.h>
 
-inline VOID 
-InsertTextAfterLoaded_RichEdit(UINT uStringID, const ATL::CStringW& szText, DWORD StringFlags, DWORD TextFlags)
+ // CAvailableApplicationInfo
+CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam)
+    : m_Parser(sFileNameParam)
 {
-    ATL::CStringW szLoadedText;
-    if (!szText.IsEmpty() && szLoadedText.LoadStringW(hInst, uStringID))
-    {
-        InsertRichEditText(szLoadedText, StringFlags);
-        InsertRichEditText(szText, TextFlags);
-    }
+    LicenseType = LICENSE_TYPE::None;
+    sFileName = sFileNameParam;
+
+    RetrieveGeneralInfo();
 }
 
-inline VOID 
-InsertLoadedTextNewl_RichEdit(UINT uStringID, DWORD StringFlags)
+VOID CAvailableApplicationInfo::RefreshAppInfo()
 {
-    ATL::CStringW szLoadedText;
-    if (szLoadedText.LoadStringW(hInst, uStringID))
+    if (szUrlDownload.IsEmpty())
     {
-        InsertRichEditText(L"\n", 0);
-        InsertRichEditText(szLoadedText, StringFlags);
-        InsertRichEditText(L"\n", 0);
+        RetrieveGeneralInfo();
     }
 }
 
-inline BOOL 
-GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString, const ATL::CStringW& cFileName)
+// Lazily load general info from the file
+VOID CAvailableApplicationInfo::RetrieveGeneralInfo()
 {
-    if (!ParserGetString(lpKeyName, cFileName, ReturnedString))
+    Category = m_Parser.GetInt(L"Category");
+
+    if (!GetString(L"Name", szName)
+        || !GetString(L"URLDownload", szUrlDownload))
     {
-        ReturnedString.Empty();
-        return FALSE;
+        return;
     }
-    return TRUE;
-}
 
-//Check both registry keys in 64bit system
-//TODO: check system type beforehand to avoid double checks?
-inline BOOL 
-GetInstalledVersionEx(PAPPLICATION_INFO Info, ATL::CStringW* szVersion, REGSAM key)
-{
-    return (!Info->szRegName.IsEmpty()
-            && (GetInstalledVersion_WowUser(szVersion, Info->szRegName, TRUE, key)
-                || GetInstalledVersion_WowUser(szVersion, Info->szRegName, FALSE, key)))
-        || (!Info->szName.IsEmpty()
-            && (GetInstalledVersion_WowUser(szVersion, Info->szName, TRUE, key)
-                || GetInstalledVersion_WowUser(szVersion, Info->szName, FALSE, key)));
+    GetString(L"RegName", szRegName);
+    GetString(L"Version", szVersion);
+    GetString(L"License", szLicense);
+    GetString(L"Description", szDesc);
+    GetString(L"Size", szSize);
+    GetString(L"URLSite", szUrlSite);
+    GetString(L"CDPath", szCDPath);
+    GetString(L"Language", szRegName);
+    GetString(L"SHA1", szSHA1);
+
+    RetrieveLicenseType();
+    RetrieveLanguages();
+    RetrieveInstalledStatus();
+    if (m_IsInstalled)
+    {
+        RetrieveInstalledVersion();
+    }
 }
 
-inline BOOL 
-GetInstalledVersion(PAPPLICATION_INFO Info, ATL::CStringW* szVersion)
+VOID CAvailableApplicationInfo::RetrieveInstalledStatus()
 {
-    return  GetInstalledVersionEx(Info, szVersion, KEY_WOW64_32KEY)
-        || GetInstalledVersionEx(Info, szVersion, KEY_WOW64_64KEY);
+    m_IsInstalled = ::GetInstalledVersion(NULL, szRegName)
+        || ::GetInstalledVersion(NULL, szName);
 }
 
-inline BOOL 
-IsAppInstalled(PAPPLICATION_INFO Info)
+VOID CAvailableApplicationInfo::RetrieveInstalledVersion()
 {
-    return GetInstalledVersion(Info, NULL);
+    m_HasInstalledVersion = ::GetInstalledVersion(&szInstalledVersion, szRegName)
+        || ::GetInstalledVersion(&szInstalledVersion, szName);
 }
 
-ATL::CSimpleArray<ATL::CStringW> 
-ParserGetAppLanguages(const ATL::CStringW& szFileName)
+VOID CAvailableApplicationInfo::RetrieveLanguages()
 {
     const WCHAR cDelimiter = L'|';
     ATL::CStringW szBuffer;
 
-    if (!ParserGetString(L"Languages", szFileName, szBuffer))
-        return CSimpleArray<ATL::CStringW>();
+    // TODO: Get multiline parameter
+    if (!m_Parser.GetString(L"Languages", szBuffer))
+    {
+        m_HasLanguageInfo = FALSE;
+        return;
+    }
 
-    ATL::CStringW szLocale;
-    ATL::CSimpleArray<ATL::CStringW> arrLocalesResult;
+    // Parse parameter string
+    ATL::CStringW m_szLocale;
+    INT iLCID;
     for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
     {
-        if (szBuffer[i] != cDelimiter)
+        if (szBuffer[i] != cDelimiter && szBuffer[i] != L'\n')
         {
-            szLocale += szBuffer[i];
-        } else {
-            arrLocalesResult.Add(szLocale);
-            szLocale.Empty();
+            m_szLocale += szBuffer[i];
+        }
+        else
+        {
+            if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+            {
+                Languages.Add(static_cast<LCID>(iLCID));
+                m_szLocale.Empty();
+            }
         }
     }
+
     // For the text after delimiter
-    if (!szLocale.IsEmpty())
+    if (!m_szLocale.IsEmpty())
     {
-        arrLocalesResult.Add(szLocale);
+        if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+        {
+            Languages.Add(static_cast<LCID>(iLCID));
+        }
     }
-    return arrLocalesResult;
+
+    m_HasLanguageInfo = TRUE;
 }
 
-BOOL
-HasNativeLanguage(PAPPLICATION_INFO Info)
+VOID CAvailableApplicationInfo::RetrieveLicenseType()
 {
-    if (!Info)
+    INT IntBuffer = m_Parser.GetInt(L"LicenseType");
+
+    if (IntBuffer < 0 || IntBuffer > LICENSE_TYPE::Max)
     {
-        return FALSE;
+        LicenseType = LICENSE_TYPE::None;
+    }
+    else
+    {
+        LicenseType = (LICENSE_TYPE) IntBuffer;
     }
-    //TODO: make the actual check
-    return TRUE;
 }
-BOOL
-HasEnglishLanguage(PAPPLICATION_INFO Info)
+
+BOOL CAvailableApplicationInfo::FindInLanguages(LCID what) const
 {
-    if (!Info)
+    if (!m_HasLanguageInfo)
     {
         return FALSE;
     }
-    //TODO: make the actual check
-    return TRUE;
-}
 
-LICENSE_TYPE
-ParserGetLicenseType(const ATL::CStringW& szFileName)
-{
-    INT IntBuffer = ParserGetInt(L"LicenseType", szFileName);
-    if (IntBuffer < 0 || IntBuffer > LICENSE_TYPE::Max)
+    //Find locale code in the list
+    const INT nLanguagesSize = Languages.GetSize();
+    for (INT i = 0; i < nLanguagesSize; ++i)
     {
-        return LICENSE_TYPE::None;
+        if (Languages[i] == what)
+        {
+            return TRUE;
+        }
     }
-    return (LICENSE_TYPE) IntBuffer;
-}
 
-LIST_ENTRY CachedEntriesHead = {&CachedEntriesHead, &CachedEntriesHead};
-PLIST_ENTRY pCachedEntry = &CachedEntriesHead;
+    return FALSE;
+}
 
-VOID 
-InsertVersionInfo_RichEdit(PAPPLICATION_INFO Info)
+BOOL CAvailableApplicationInfo::HasLanguageInfo() const
 {
-    ATL::CStringW szVersion;
-    BOOL bIsInstalled = IsAppInstalled(Info),
-        bHasVersion = GetInstalledVersion(Info, &szVersion);
+    return m_HasLanguageInfo;
+}
 
-    if (bIsInstalled)
-    {
-        if (bHasVersion)
-        {
-            if (Info->szVersion.Compare(szVersion) > 0)
-                InsertLoadedTextNewl_RichEdit(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
-            else
-                InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
+BOOL CAvailableApplicationInfo::HasNativeLanguage() const
+{
+    return FindInLanguages(GetUserDefaultLCID());
+}
 
-            InsertTextAfterLoaded_RichEdit(IDS_AINFO_VERSION, szVersion, CFE_BOLD, 0);
-        }
-        else
-        {
-            InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
-        }
-    }
-    else
-    {
-        InsertLoadedTextNewl_RichEdit(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
-    }
-    
-    InsertTextAfterLoaded_RichEdit(IDS_AINFO_AVAILABLEVERSION, Info->szVersion, CFE_BOLD, 0);
+BOOL CAvailableApplicationInfo::HasEnglishLanguage() const
+{
+    return FindInLanguages(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT));
 }
 
-VOID
-InsertLicenseInfo_RichEdit(PAPPLICATION_INFO Info)
+BOOL CAvailableApplicationInfo::IsInstalled() const
 {
-    ATL::CStringW szLicense;
-    switch (Info->LicenseType)
-    {
-    case LICENSE_TYPE::OpenSource:
-        szLicense.LoadStringW(hInst, IDS_LICENSE_OPENSOURCE);
-        break;
-    case LICENSE_TYPE::Freeware:
-        szLicense.LoadStringW(hInst, IDS_LICENSE_FREEWARE);
-        break;
-    case LICENSE_TYPE::Trial:
-        szLicense.LoadStringW(hInst, IDS_LICENSE_TRIAL);
-        break;
-    default:
-        break;
-    }
+    return m_IsInstalled;
+}
 
-    if (szLicense.IsEmpty())
-    {
-        InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
-    }
-    else
-    {
-        szLicense.Format(L"(%ls)", Info->szLicense);
-        InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
-    }
+BOOL CAvailableApplicationInfo::HasInstalledVersion() const
+{
+    return m_HasInstalledVersion;
+}
 
+BOOL CAvailableApplicationInfo::HasUpdate() const
+{
+    return (szInstalledVersion.Compare(szVersion) < 0) ? TRUE : FALSE;
 }
 
-VOID
-InsertLanguageInfo_RichEdit(PAPPLICATION_INFO Info)
+VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime)
 {
-    const INT nTranslations = Info->Languages.GetSize();
-    ATL::CStringW szLangInfo;
-    ATL::CStringW szLoadedTextAvailability;
-    ATL::CStringW szLoadedAInfoText;
-    szLoadedAInfoText.LoadStringW(IDS_AINFO_LANGUAGES);
+    RtlCopyMemory(&ftCacheStamp, ftTime, sizeof(FILETIME));
+}
 
-    if(HasNativeLanguage(Info))
-    {
-        szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_AVAILABLE_TRANSLATION);
-    }
-    else if (HasEnglishLanguage(Info))
-    {
-        szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_ENGLISH_TRANSLATION);
-    }
-    else
+inline BOOL CAvailableApplicationInfo::GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString)
+{
+    if (!m_Parser.GetString(lpKeyName, ReturnedString))
     {
-        szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_NO_TRANSLATION);
+        ReturnedString.Empty();
+        return FALSE;
     }
+    return TRUE;
+}
+// CAvailableApplicationInfo 
 
-    if (nTranslations > 1)
+// CAvailableApps
+CAvailableApps::CAvailableApps()
+{
+    //set all paths
+    if (GetStorageDirectory(m_szPath))
     {
-        szLangInfo.Format(L" (+%d more)", nTranslations - 1);
+        m_szAppsPath = m_szPath + L"\\rapps\\";
+        m_szCabPath = m_szPath + L"\\rappmgr.cab";
+        m_szSearchPath = m_szAppsPath + L"*.txt";
     }
-
-    InsertRichEditText(szLoadedAInfoText, CFE_BOLD);
-    InsertRichEditText(szLoadedTextAvailability, NULL);
-    InsertRichEditText(szLangInfo, CFE_ITALIC);
 }
 
-BOOL
-ShowAvailableAppInfo(INT Index)
+VOID CAvailableApps::FreeCachedEntries()
 {
-    PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
-    if (!Info) return FALSE;
+    POSITION InfoListPosition = m_InfoList.GetHeadPosition();
 
-    NewRichEditText(Info->szName, CFE_BOLD);
-    InsertVersionInfo_RichEdit(Info);
-    InsertLicenseInfo_RichEdit(Info);
-    InsertLanguageInfo_RichEdit(Info);
-
-    InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
-    InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
-    InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
-    InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->szUrlDownload, CFE_BOLD, CFE_LINK);
+    /* loop and deallocate all the cached app infos in the list */
+    while (InfoListPosition)
+    {
+        CAvailableApplicationInfo* Info = m_InfoList.GetAt(InfoListPosition);
+        m_InfoList.RemoveHead();
+        delete Info;
 
-    return TRUE;
+        InfoListPosition = m_InfoList.GetHeadPosition();
+    }
 }
 
-static BOOL
-DeleteCurrentAppsDB(VOID)
+VOID CAvailableApps::DeleteCurrentAppsDB()
 {
     HANDLE hFind = INVALID_HANDLE_VALUE;
     WIN32_FIND_DATAW FindFileData;
-    ATL::CStringW szCabPath;
-    ATL::CStringW szSearchPath;
-    ATL::CStringW szPath;
-    BOOL result = TRUE;
-
-    if (!GetStorageDirectory(szPath))
-        return FALSE;
-
-    szCabPath = szPath + L"\\rappmgr.cab";
-    result = result && DeleteFileW(szCabPath.GetString());
 
-    szPath += L"\\rapps\\";
-    szSearchPath = szPath + L"*.txt";
+    if (m_szPath.IsEmpty())
+        return;
 
-    hFind = FindFirstFileW(szSearchPath.GetString(), &FindFileData);
+    DeleteFileW(m_szCabPath.GetString());
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
-    if (hFind == INVALID_HANDLE_VALUE)
-        return result;
-
-    ATL::CStringW szTmp;
-    do
+    if (hFind != INVALID_HANDLE_VALUE)
     {
-        szTmp = szPath + FindFileData.cFileName;
-        result = result && DeleteFileW(szTmp.GetString());
-    } while (FindNextFileW(hFind, &FindFileData) != 0);
-
-    FindClose(hFind);
-
-    return result;
+        ATL::CStringW szTmp;
+        do
+        {
+            szTmp = m_szPath + FindFileData.cFileName;
+            DeleteFileW(szTmp.GetString());
+        } while (FindNextFileW(hFind, &FindFileData) != 0);
+        FindClose(hFind);
+    }
 }
 
-BOOL
-UpdateAppsDB(VOID)
+BOOL CAvailableApps::UpdateAppsDB()
 {
-    ATL::CStringW szPath;
-    ATL::CStringW szAppsPath;
-    ATL::CStringW szCabPath;
-
-    if (!DeleteCurrentAppsDB())
-        return FALSE;
+    DeleteCurrentAppsDB();
 
-    DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+    CDownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
 
-    if (!GetStorageDirectory(szPath))
+    if (m_szPath.IsEmpty())
         return FALSE;
 
-    szCabPath = szPath + L"\\rappmgr.cab";
-    szAppsPath = szPath + L"\\rapps\\";
-
-    if (!ExtractFilesFromCab(szCabPath, szAppsPath))
+    if (!ExtractFilesFromCab(m_szCabPath, m_szAppsPath))
     {
         return FALSE;
     }
@@ -311,90 +267,71 @@ UpdateAppsDB(VOID)
     return TRUE;
 }
 
-
-BOOL
-EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
+BOOL CAvailableApps::EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
 {
     HANDLE hFind = INVALID_HANDLE_VALUE;
     WIN32_FIND_DATAW FindFileData;
-    ATL::CStringW szPath;
-    ATL::CStringW szAppsPath;
-    ATL::CStringW szCabPath;
-    PAPPLICATION_INFO Info;
 
-    if (!GetStorageDirectory(szPath))
-        return FALSE;
-
-    szCabPath = szPath + L"\\rappmgr.cab";
-    szPath += L"\\rapps\\";
-    szAppsPath = szPath;
-
-    if (!CreateDirectoryW(szPath.GetString(), NULL) &&
+    if (!CreateDirectoryW(m_szPath.GetString(), NULL) &&
         GetLastError() != ERROR_ALREADY_EXISTS)
     {
         return FALSE;
     }
 
-    szPath += L"*.txt";
-    hFind = FindFirstFileW(szPath.GetString(), &FindFileData);
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
     if (hFind == INVALID_HANDLE_VALUE)
     {
-        if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
-            DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+        if(!UpdateAppsDB()) {
+            return FALSE;
+        }
 
-        ExtractFilesFromCab(szCabPath, szAppsPath);
-        hFind = FindFirstFileW(szPath, &FindFileData);
+        hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
         if (hFind == INVALID_HANDLE_VALUE)
+        {
             return FALSE;
+        }
     }
 
     do
     {
-        /* loop for all the cached entries */
-        POSITION CurrentListPosition = InfoList.GetHeadPosition();
+        // loop for all the cached entries
+        POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
+        CAvailableApplicationInfo* Info = NULL;
 
         while (CurrentListPosition != NULL)
         {
             POSITION LastListPosition = CurrentListPosition;
-            Info = InfoList.GetNext(CurrentListPosition);
+            Info = m_InfoList.GetNext(CurrentListPosition);
 
-            /* do we already have this entry in cache? */
-            if (!Info->cFileName.Compare(FindFileData.cFileName))
+            // do we already have this entry in cache?
+            if (Info->sFileName == FindFileData.cFileName)
             {
-                /* is it current enough, or the file has been modified since our last time here? */
+                // is it current enough, or the file has been modified since our last time here?
                 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
                 {
-                    /* recreate our cache, this is the slow path */
-                    InfoList.RemoveAt(LastListPosition);
+                    // recreate our cache, this is the slow path
+                    m_InfoList.RemoveAt(LastListPosition);
+
                     delete Info;
+                    Info = NULL;
                     break;
                 }
                 else
                 {
-                    /* speedy path, compare directly, we already have the data */
+                    // speedy path, compare directly, we already have the data
                     goto skip_if_cached;
                 }
             }
         }
 
-        /* create a new entry */
-        Info = new APPLICATION_INFO();
-
-        if (!Info)
-            break;
-
-        Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
-        Info->LicenseType = ParserGetLicenseType(FindFileData.cFileName);
-        Info->Languages = ParserGetAppLanguages(FindFileData.cFileName);
+        // create a new entry
+        Info = new CAvailableApplicationInfo(FindFileData.cFileName);
 
-        /* copy the cache-related fields for the next time */
-        Info->cFileName = FindFileData.cFileName;
-        RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
-
-        /* add our cached entry to the cached list */
-        InfoList.AddTail(Info);
+        // set a timestamp for the next time
+        Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
+        m_InfoList.AddTail(Info);
 
 skip_if_cached:
         if (Info->Category == FALSE)
@@ -403,50 +340,79 @@ skip_if_cached:
         if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
             continue;
 
-        /* if our cache hit was only partial, we need to parse
-           and lazily fill the rest of fields only when needed */
-
-        if (Info->szUrlDownload.IsEmpty())
-        {
-            if (!GetString(L"Name", Info->szName, FindFileData.cFileName)
-                || !GetString(L"URLDownload", Info->szUrlDownload, FindFileData.cFileName))
-            {
-                continue;
-            }
-
-            GetString(L"RegName", Info->szRegName, FindFileData.cFileName);
-            GetString(L"Version", Info->szVersion, FindFileData.cFileName);
-            GetString(L"License", Info->szLicense, FindFileData.cFileName);
-            GetString(L"Description", Info->szDesc, FindFileData.cFileName);
-            GetString(L"Size", Info->szSize, FindFileData.cFileName);
-            GetString(L"URLSite", Info->szUrlSite, FindFileData.cFileName);
-            GetString(L"CDPath", Info->szCDPath, FindFileData.cFileName);
-            GetString(L"Language", Info->szRegName, FindFileData.cFileName);
-            GetString(L"SHA1", Info->szSHA1, FindFileData.cFileName);
-        }
+        Info->RefreshAppInfo();
 
-        if (!lpEnumProc(Info))
-            break;
+        if (lpEnumProc)
+            lpEnumProc(static_cast<PAPPLICATION_INFO>(Info), m_szAppsPath.GetString());
 
     } while (FindNextFileW(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
-
     return TRUE;
 }
 
-VOID FreeCachedAvailableEntries(VOID)
+const PAPPLICATION_INFO CAvailableApps::FindInfo(const ATL::CStringW& szAppName)
 {
-    PAPPLICATION_INFO Info;
-    POSITION InfoListPosition = InfoList.GetHeadPosition();
+    if (m_InfoList.IsEmpty())
+    {
+        return NULL;
+    }
 
-    /* loop and deallocate all the cached app infos in the list */
-    while (InfoListPosition)
+    // linear search
+    POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
+    PAPPLICATION_INFO info;
+    while (CurrentListPosition != NULL)
     {
-        Info = InfoList.GetAt(InfoListPosition);
-        InfoList.RemoveHead();
-        InfoListPosition = InfoList.GetHeadPosition();
-        
-        delete Info;
+        info = m_InfoList.GetNext(CurrentListPosition);
+        if (info->szName == szAppName)
+        {
+            return info;
+        }
     }
-}
\ No newline at end of file
+    return NULL;
+}
+
+ATL::CSimpleArray<PAPPLICATION_INFO> CAvailableApps::FindInfoList(const ATL::CSimpleArray<ATL::CStringW> &arrAppsNames)
+{
+    ATL::CSimpleArray<PAPPLICATION_INFO> result;
+    for (INT i = 0; i < arrAppsNames.GetSize(); ++i)
+    {
+        PAPPLICATION_INFO Info = FindInfo(arrAppsNames[i]);
+        if (Info)
+        {
+            result.Add(Info);
+        }
+    }
+    return result;
+}
+
+const ATL::CStringW& CAvailableApps::GetFolderPath()
+{
+    return m_szPath;
+}
+
+const ATL::CStringW& CAvailableApps::GetAppPath()
+{
+    return m_szAppsPath;
+}
+
+const ATL::CStringW& CAvailableApps::GetCabPath()
+{
+    return m_szCabPath;
+}
+
+const LPCWSTR CAvailableApps::GetFolderPathString()
+{
+    return m_szPath.GetString();
+}
+
+const LPCWSTR CAvailableApps::GetAppPathString()
+{
+    return m_szPath.GetString();
+}
+
+const LPCWSTR CAvailableApps::GetCabPathString()
+{
+    return m_szPath.GetString();
+}
+// CAvailableApps