[RAPPS] Replaced my @gmail.com email with @reactos,org one & Removed my copyright...
[reactos.git] / reactos / base / applications / rapps / available.cpp
index eb74972..69817da 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
- * PROGRAMMERS:     Dmitry Chapyshev           (dmitry@reactos.org)
- *                  Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
+ * PROJECT:     ReactOS Applications Manager
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * FILE:        base/applications/rapps/available.cpp
+ * PURPOSE:     Classes for working with available applications
+ * COPYRIGHT:   Copyright 2009 Dmitry Chapyshev           (dmitry@reactos.org)
+ *              Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
+ *              Copyright 2017 Alexander Shaposhnikov     (sanchaez@reactos.org)
  */
+#include "defines.h"
 
-#include "rapps.h"
+#include "available.h"
+#include "misc.h"
+#include "dialogs.h"
 
-#define ADD_TEXT(a, b, c, d) \
-    if (b[0] != '\0') \
-    { \
-        LoadStringW(hInst, a, szText, _countof(szText)); \
-        InsertRichEditText(szText, c); \
-        InsertRichEditText(b, d); \
-    } \
+#include <atlcoll.h>
+#include <atlsimpcoll.h>
+#include <atlstr.h>
 
-#define GET_STRING1(a, b)  \
-    if (!ParserGetString(a, b, _countof(b), FindFileData.cFileName)) \
-        continue;
+ // CAvailableApplicationInfo
+CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam)
+    : m_Parser(sFileNameParam)
+{
+    m_LicenseType = LicenseType::LICENSE_NONE;
 
-#define GET_STRING2(a, b)  \
-    if (!ParserGetString(a, b, _countof(b), FindFileData.cFileName)) \
-        b[0] = '\0';
+    m_sFileName = sFileNameParam;
 
-LIST_ENTRY CachedEntriesHead = { &CachedEntriesHead, &CachedEntriesHead };
-PLIST_ENTRY pCachedEntry = &CachedEntriesHead;
+    RetrieveGeneralInfo();
+}
 
-BOOL
-ShowAvailableAppInfo(INT Index)
+VOID CAvailableApplicationInfo::RefreshAppInfo()
 {
-    PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
-    WCHAR szText[MAX_STR_LEN];
+    if (m_szUrlDownload.IsEmpty())
+    {
+        RetrieveGeneralInfo();
+    }
+}
 
-    if (!Info) return FALSE;
+// Lazily load general info from the file
+VOID CAvailableApplicationInfo::RetrieveGeneralInfo()
+{
+    m_Category = m_Parser.GetInt(L"Category");
 
-    NewRichEditText(Info->szName, CFE_BOLD);
+    if (!GetString(L"Name", m_szName)
+        || !GetString(L"URLDownload", m_szUrlDownload))
+    {
+        return;
+    }
 
-    InsertRichEditText(L"\n", 0);
+    GetString(L"RegName", m_szRegName);
+    GetString(L"Version", m_szVersion);
+    GetString(L"License", m_szLicense);
+    GetString(L"Description", m_szDesc);
+    GetString(L"Size", m_szSize);
+    GetString(L"URLSite", m_szUrlSite);
+    GetString(L"CDPath", m_szCDPath);
+    GetString(L"Language", m_szRegName);
+    GetString(L"SHA1", m_szSHA1);
+
+    RetrieveLicenseType();
+    RetrieveLanguages();
+    RetrieveInstalledStatus();
+    if (m_IsInstalled)
+    {
+        RetrieveInstalledVersion();
+    }
+}
 
-    ADD_TEXT(IDS_AINFO_VERSION,     Info->szVersion, CFE_BOLD, 0);
-    ADD_TEXT(IDS_AINFO_LICENSE,     Info->szLicense, CFE_BOLD, 0);
-    ADD_TEXT(IDS_AINFO_SIZE,        Info->szSize,    CFE_BOLD, 0);
-    ADD_TEXT(IDS_AINFO_URLSITE,     Info->szUrlSite, CFE_BOLD, CFE_LINK);
-    ADD_TEXT(IDS_AINFO_DESCRIPTION, Info->szDesc,    CFE_BOLD, 0);
+VOID CAvailableApplicationInfo::RetrieveInstalledStatus()
+{
+    m_IsInstalled = ::GetInstalledVersion(NULL, m_szRegName)
+        || ::GetInstalledVersion(NULL, m_szName);
+}
 
-    return TRUE;
+VOID CAvailableApplicationInfo::RetrieveInstalledVersion()
+{
+    ATL::CStringW szNameVersion = m_szName + L" " + m_szVersion;
+    m_HasInstalledVersion = ::GetInstalledVersion(&m_szInstalledVersion, m_szRegName)
+        || ::GetInstalledVersion(&m_szInstalledVersion, m_szName)
+        || ::GetInstalledVersion(&m_szInstalledVersion, szNameVersion);
 }
 
-static BOOL
-DeleteCurrentAppsDB(VOID)
+VOID CAvailableApplicationInfo::RetrieveLanguages()
 {
-    HANDLE hFind = INVALID_HANDLE_VALUE;
-    WIN32_FIND_DATAW FindFileData;
-    WCHAR szCabPath[MAX_PATH];
-    WCHAR szSearchPath[MAX_PATH];
-    WCHAR szPath[MAX_PATH];
-    WCHAR szTmp[MAX_PATH];
-    HRESULT hr;
-    BOOL result = TRUE;
-
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
+    const WCHAR cDelimiter = L'|';
+    ATL::CStringW szBuffer;
 
-    hr = StringCbPrintfW(szCabPath, sizeof(szCabPath),
-                         L"%ls\\rappmgr.cab",
-                         szPath);
-    if (FAILED(hr))
-        return FALSE;
+    // TODO: Get multiline parameter
+    if (!m_Parser.GetString(L"Languages", szBuffer))
+    {
+        m_HasLanguageInfo = FALSE;
+        return;
+    }
 
-    result = result && DeleteFileW(szCabPath);
+    // Parse parameter string
+    ATL::CStringW m_szLocale;
+    INT iLCID;
+    for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
+    {
+        if (szBuffer[i] != cDelimiter && szBuffer[i] != L'\n')
+        {
+            m_szLocale += szBuffer[i];
+        }
+        else
+        {
+            if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+            {
+                m_LanguageLCIDs.Add(static_cast<LCID>(iLCID));
+                m_szLocale.Empty();
+            }
+        }
+    }
 
-    hr = StringCbCatW(szPath, sizeof(szPath), L"\\rapps\\");
+    // For the text after delimiter
+    if (!m_szLocale.IsEmpty())
+    {
+        if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+        {
+            m_LanguageLCIDs.Add(static_cast<LCID>(iLCID));
+        }
+    }
 
-    if (FAILED(hr))
-        return FALSE;
+    m_HasLanguageInfo = TRUE;
+}
 
-    hr = StringCbPrintfW(szSearchPath, sizeof(szSearchPath),
-                         L"%ls*.txt",
-                         szPath);
-    if (FAILED(hr))
-        return FALSE;
+VOID CAvailableApplicationInfo::RetrieveLicenseType()
+{
+    INT IntBuffer = m_Parser.GetInt(L"LicenseType");
 
-    hFind = FindFirstFileW(szSearchPath, &FindFileData);
+    if (IsLicenseType(IntBuffer))
+    {
+        m_LicenseType = static_cast<LicenseType>(IntBuffer);
+    }
+    else
+    {
+        m_LicenseType = LicenseType::LICENSE_NONE;
+    }
+}
 
-    if (hFind == INVALID_HANDLE_VALUE)
-        return result;
+BOOL CAvailableApplicationInfo::FindInLanguages(LCID what) const
+{
+    if (!m_HasLanguageInfo)
+    {
+        return FALSE;
+    }
 
-    do
+    //Find locale code in the list
+    const INT nLanguagesSize = m_LanguageLCIDs.GetSize();
+    for (INT i = 0; i < nLanguagesSize; ++i)
     {
-        hr = StringCbPrintfW(szTmp, sizeof(szTmp),
-                             L"%ls%ls",
-                             szPath, FindFileData.cFileName);
-        if (FAILED(hr))
-            continue;
-        result = result && DeleteFileW(szTmp);
+        if (m_LanguageLCIDs[i] == what)
+        {
+            return TRUE;
+        }
+    }
 
-    } while (FindNextFileW(hFind, &FindFileData) != 0);
+    return FALSE;
+}
 
-    FindClose(hFind);
+BOOL CAvailableApplicationInfo::HasLanguageInfo() const
+{
+    return m_HasLanguageInfo;
+}
 
-    return result;
+BOOL CAvailableApplicationInfo::HasNativeLanguage() const
+{
+    return FindInLanguages(GetUserDefaultLCID());
 }
 
+BOOL CAvailableApplicationInfo::HasEnglishLanguage() const
+{
+    return FindInLanguages(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT));
+}
 
-BOOL
-UpdateAppsDB(VOID)
+BOOL CAvailableApplicationInfo::IsInstalled() const
 {
-    WCHAR szPath[MAX_PATH];
-    WCHAR szAppsPath[MAX_PATH];
-    WCHAR szCabPath[MAX_PATH];
+    return m_IsInstalled;
+}
 
-    if (!DeleteCurrentAppsDB())
-        return FALSE;
+BOOL CAvailableApplicationInfo::HasInstalledVersion() const
+{
+    return m_HasInstalledVersion;
+}
 
-    DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+BOOL CAvailableApplicationInfo::HasUpdate() const
+{
+    return (m_szInstalledVersion.Compare(m_szVersion) < 0) ? TRUE : FALSE;
+}
 
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
+VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime)
+{
+    RtlCopyMemory(&m_ftCacheStamp, ftTime, sizeof(FILETIME));
+}
 
-    if (FAILED(StringCbPrintfW(szCabPath, sizeof(szCabPath),
-                               L"%ls\\rappmgr.cab",
-                               szPath)))
+inline BOOL CAvailableApplicationInfo::GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString)
+{
+    if (!m_Parser.GetString(lpKeyName, ReturnedString))
     {
+        ReturnedString.Empty();
         return FALSE;
     }
+    return TRUE;
+}
+// CAvailableApplicationInfo 
+
+// CAvailableApps
+ATL::CStringW CAvailableApps::m_szPath;
+ATL::CStringW CAvailableApps::m_szCabPath;
+ATL::CStringW CAvailableApps::m_szAppsPath;
+ATL::CStringW CAvailableApps::m_szSearchPath;
 
-    if (FAILED(StringCbPrintfW(szAppsPath, sizeof(szAppsPath),
-                               L"%ls\\rapps\\",
-                               szPath)))
+BOOL CAvailableApps::InitializeStaticStrings()
+{
+
+    if (!m_szPath.IsEmpty())
     {
-        return FALSE;
+        // strings are filled
+        return TRUE;
+    }
+
+    //FIXME: maybe provide a fallback?
+    if (GetStorageDirectory(m_szPath))
+    {
+        m_szAppsPath = m_szPath + L"\\rapps\\";
+        m_szCabPath = m_szPath + L"\\rappmgr.cab";
+        m_szSearchPath = m_szAppsPath + L"*.txt";
+        return TRUE;
     }
 
-    ExtractFilesFromCab(szCabPath, szAppsPath);
+    return FALSE;
+}
 
-    return TRUE;
+CAvailableApps::CAvailableApps()
+{
+    //set all paths
+    InitializeStaticStrings();
 }
 
+VOID CAvailableApps::FreeCachedEntries()
+{
+    POSITION InfoListPosition = m_InfoList.GetHeadPosition();
+
+    /* loop and deallocate all the cached app infos in the list */
+    while (InfoListPosition)
+    {
+        CAvailableApplicationInfo* Info = m_InfoList.GetNext(InfoListPosition);
+        delete Info;
+    }
+
+    m_InfoList.RemoveAll();
+}
 
-BOOL
-EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
+VOID CAvailableApps::DeleteCurrentAppsDB()
 {
     HANDLE hFind = INVALID_HANDLE_VALUE;
     WIN32_FIND_DATAW FindFileData;
-    WCHAR szPath[MAX_PATH];
-    WCHAR szAppsPath[MAX_PATH];
-    WCHAR szCabPath[MAX_PATH];
-    PAPPLICATION_INFO Info;
-    HRESULT hr;
 
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
+    if (!InitializeStaticStrings())
+    {
+        return;
+    }
 
-    hr = StringCbPrintfW(szCabPath, sizeof(szCabPath),
-                         L"%ls\\rappmgr.cab",
-                         szPath);
-    if (FAILED(hr))
-        return FALSE;
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
-    hr = StringCbCatW(szPath, sizeof(szPath), L"\\rapps\\");
+    if (hFind != INVALID_HANDLE_VALUE)
+    {
+        ATL::CStringW szTmp;
+        do
+        {
+            szTmp = m_szAppsPath + FindFileData.cFileName;
+            DeleteFileW(szTmp.GetString());
+        } while (FindNextFileW(hFind, &FindFileData) != 0);
+        FindClose(hFind);
+    }
 
-    if (FAILED(hr))
-        return FALSE;
+    RemoveDirectoryW(m_szAppsPath);
+    RemoveDirectoryW(m_szPath);
+}
 
-    hr = StringCbCopyW(szAppsPath, sizeof(szAppsPath), szPath);
+BOOL CAvailableApps::UpdateAppsDB()
+{
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    WIN32_FIND_DATAW FindFileData;
 
-    if (FAILED(hr))
+    if (!InitializeStaticStrings())
+    {
         return FALSE;
+    }
 
-    if (!CreateDirectory(szPath, NULL) &&
-        GetLastError() != ERROR_ALREADY_EXISTS)
+    if (!CreateDirectoryW(m_szPath.GetString(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
     {
         return FALSE;
     }
 
-    hr = StringCbCatW(szPath, sizeof(szPath), L"*.txt");
+    //if there are some files in the db folder - we're good
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
+    if (hFind != INVALID_HANDLE_VALUE)
+    {
+        return TRUE;
+    }
+
+    CDownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
 
-    if (FAILED(hr))
+    if (!ExtractFilesFromCab(m_szCabPath, m_szAppsPath))
+    {
         return FALSE;
+    }
 
-    hFind = FindFirstFileW(szPath, &FindFileData);
+    DeleteFileW(m_szCabPath.GetString());
 
-    if (hFind == INVALID_HANDLE_VALUE)
-    {
-        if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
-            DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+    return TRUE;
+}
 
-        ExtractFilesFromCab(szCabPath, szAppsPath);
-        hFind = FindFirstFileW(szPath, &FindFileData);
+BOOL CAvailableApps::ForceUpdateAppsDB()
+{
+    DeleteCurrentAppsDB();
+    return UpdateAppsDB();
+}
 
-        if (hFind == INVALID_HANDLE_VALUE)
-            return FALSE;
+BOOL CAvailableApps::Enum(INT EnumType, AVAILENUMPROC lpEnumProc)
+{
+
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    WIN32_FIND_DATAW FindFileData;
+
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
+
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        //no db yet
+        return FALSE;
     }
 
     do
     {
-        /* loop for all the cached entries */
-        for (pCachedEntry = CachedEntriesHead.Flink; pCachedEntry != &CachedEntriesHead; pCachedEntry = pCachedEntry->Flink)
+        // loop for all the cached entries
+        POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
+        CAvailableApplicationInfo* Info = NULL;
+
+        while (CurrentListPosition != NULL)
         {
-            Info = CONTAINING_RECORD(pCachedEntry, APPLICATION_INFO, List);
+            POSITION LastListPosition = CurrentListPosition;
+            Info = m_InfoList.GetNext(CurrentListPosition);
 
-            /* do we already have this entry in cache? */
-            if(_wcsicmp(FindFileData.cFileName, Info->cFileName) == 0)
+            // do we already have this entry in cache?
+            if (Info->m_sFileName == FindFileData.cFileName)
             {
-                /* is it current enough, or the file has been modified since our last time here? */
-                if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
+                // is it current enough, or the file has been modified since our last time here?
+                if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->m_ftCacheStamp) == 1)
                 {
-                    /* recreate our cache, this is the slow path */
-                    RemoveEntryList(&Info->List);
-                    HeapFree(GetProcessHeap(), 0, Info);
+                    // 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;
                 }
-
-                break;
             }
         }
 
-        /* create a new entry */
-        Info = (PAPPLICATION_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APPLICATION_INFO));
-
-        if(!Info)
-            break;
-
-        Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
+        // create a new entry
+        Info = new CAvailableApplicationInfo(FindFileData.cFileName);
 
-        /* copy the cache-related fields for the next time */
-        RtlCopyMemory(&Info->cFileName,    &FindFileData.cFileName, MAX_PATH);
-        RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
-
-        /* add our cached entry to the cached list */
-        InsertTailList(&CachedEntriesHead, &Info->List);
+        // set a timestamp for the next time
+        Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
+        m_InfoList.AddTail(Info);
 
 skip_if_cached:
-
-        if (Info->Category == FALSE)
+        if (Info->m_Category == FALSE)
             continue;
 
-        if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
+        if (EnumType != Info->m_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 */
+        Info->RefreshAppInfo();
 
-        if (Info->szUrlDownload[0] == 0)
-        {
-            GET_STRING1(L"Name",        Info->szName);
-            GET_STRING1(L"URLDownload", Info->szUrlDownload);
-
-            GET_STRING2(L"RegName",     Info->szRegName);
-            GET_STRING2(L"Version",     Info->szVersion);
-            GET_STRING2(L"License",     Info->szLicense);
-            GET_STRING2(L"Description", Info->szDesc);
-            GET_STRING2(L"Size",        Info->szSize);
-            GET_STRING2(L"URLSite",     Info->szUrlSite);
-            GET_STRING2(L"CDPath",      Info->szCDPath);
-            GET_STRING2(L"SHA1",        Info->szSHA1);
-        }
-
-        if (!lpEnumProc(Info))
-            break;
+        if (lpEnumProc)
+            lpEnumProc(static_cast<CAvailableApplicationInfo*>(Info), m_szAppsPath.GetString());
 
     } while (FindNextFileW(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
-
     return TRUE;
 }
 
-VOID FreeCachedAvailableEntries(VOID)
+CAvailableApplicationInfo* CAvailableApps::FindInfo(const ATL::CStringW& szAppName) const
 {
-     PAPPLICATION_INFO Info;
-    /* loop and deallocate all the cached app infos in the list */
-    for (pCachedEntry = CachedEntriesHead.Flink; pCachedEntry != &CachedEntriesHead;)
+    if (m_InfoList.IsEmpty())
     {
-         Info = CONTAINING_RECORD(pCachedEntry, APPLICATION_INFO, List);
-        /* grab a reference to the next linked entry before getting rid of the current one */
-        pCachedEntry = pCachedEntry->Flink;
-        /* flush them down the toilet :D */
-        RemoveEntryList(&Info->List);
-        HeapFree(GetProcessHeap(), 0, Info);
+        return NULL;
+    }
+
+    // linear search
+    POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
+    CAvailableApplicationInfo* info;
+    while (CurrentListPosition != NULL)
+    {
+        info = m_InfoList.GetNext(CurrentListPosition);
+        if (info->m_szName == szAppName)
+        {
+            return info;
+        }
+    }
+    return NULL;
+}
+
+ATL::CSimpleArray<CAvailableApplicationInfo*> CAvailableApps::FindInfoList(const ATL::CSimpleArray<ATL::CStringW> &arrAppsNames) const
+{
+    ATL::CSimpleArray<CAvailableApplicationInfo*> result;
+    for (INT i = 0; i < arrAppsNames.GetSize(); ++i)
+    {
+        CAvailableApplicationInfo* Info = FindInfo(arrAppsNames[i]);
+        if (Info)
+        {
+            result.Add(Info);
+        }
     }
-}
\ No newline at end of file
+    return result;
+}
+
+const ATL::CStringW& CAvailableApps::GetFolderPath() const
+{
+    return m_szPath;
+}
+
+const ATL::CStringW& CAvailableApps::GetAppPath() const
+{
+    return m_szAppsPath;
+}
+
+const ATL::CStringW& CAvailableApps::GetCabPath() const
+{
+    return m_szCabPath;
+}
+
+LPCWSTR CAvailableApps::GetFolderPathString() const
+{
+    return m_szPath.GetString();
+}
+
+LPCWSTR CAvailableApps::GetAppPathString() const
+{
+    return m_szPath.GetString();
+}
+
+LPCWSTR CAvailableApps::GetCabPathString() const
+{
+    return m_szPath.GetString();
+}
+// CAvailableApps