/*
- * 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