1a9b1e059SJoachim Henze /*
2a9b1e059SJoachim Henze * PROJECT: ReactOS Applications Manager
3a9b1e059SJoachim Henze * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4a9b1e059SJoachim Henze * PURPOSE: Classes for working with available applications
5a9b1e059SJoachim Henze * COPYRIGHT: Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
6a9b1e059SJoachim Henze * Copyright 2020 He Yang (1160386205@qq.com)
7a9b1e059SJoachim Henze * Copyright 2021-2023 Mark Jansen <mark.jansen@reactos.org>
8a9b1e059SJoachim Henze */
9a9b1e059SJoachim Henze
10a9b1e059SJoachim Henze #include "rapps.h"
11a9b1e059SJoachim Henze #include "appdb.h"
12a9b1e059SJoachim Henze #include "configparser.h"
13a9b1e059SJoachim Henze #include "settings.h"
14a9b1e059SJoachim Henze
15a9b1e059SJoachim Henze
16a9b1e059SJoachim Henze static HKEY g_RootKeyEnum[3] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE};
17*57b775efSWhindmar Saksit static REGSAM g_RegSamEnum[3] = {0, KEY_WOW64_32KEY, KEY_WOW64_64KEY};
18a9b1e059SJoachim Henze #define UNINSTALL_SUBKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
19a9b1e059SJoachim Henze
20a9b1e059SJoachim Henze static VOID
ClearList(CAtlList<CAppInfo * > & list)21a9b1e059SJoachim Henze ClearList(CAtlList<CAppInfo *> &list)
22a9b1e059SJoachim Henze {
23a9b1e059SJoachim Henze POSITION InfoListPosition = list.GetHeadPosition();
24a9b1e059SJoachim Henze while (InfoListPosition)
25a9b1e059SJoachim Henze {
26a9b1e059SJoachim Henze CAppInfo *Info = list.GetNext(InfoListPosition);
27a9b1e059SJoachim Henze delete Info;
28a9b1e059SJoachim Henze }
29a9b1e059SJoachim Henze list.RemoveAll();
30a9b1e059SJoachim Henze }
31a9b1e059SJoachim Henze
CAppDB(const CStringW & path)32a9b1e059SJoachim Henze CAppDB::CAppDB(const CStringW &path) : m_BasePath(path)
33a9b1e059SJoachim Henze {
34a9b1e059SJoachim Henze m_BasePath.Canonicalize();
35a9b1e059SJoachim Henze }
36a9b1e059SJoachim Henze
37*57b775efSWhindmar Saksit CAvailableApplicationInfo *
FindAvailableByPackageName(const CStringW & name)38*57b775efSWhindmar Saksit CAppDB::FindAvailableByPackageName(const CStringW &name)
39a9b1e059SJoachim Henze {
40a9b1e059SJoachim Henze POSITION CurrentListPosition = m_Available.GetHeadPosition();
41a9b1e059SJoachim Henze while (CurrentListPosition)
42a9b1e059SJoachim Henze {
43a9b1e059SJoachim Henze CAppInfo *Info = m_Available.GetNext(CurrentListPosition);
44a9b1e059SJoachim Henze if (Info->szIdentifier == name)
45a9b1e059SJoachim Henze {
46*57b775efSWhindmar Saksit return static_cast<CAvailableApplicationInfo *>(Info);
47a9b1e059SJoachim Henze }
48a9b1e059SJoachim Henze }
49a9b1e059SJoachim Henze return NULL;
50a9b1e059SJoachim Henze }
51a9b1e059SJoachim Henze
52a9b1e059SJoachim Henze void
GetApps(CAtlList<CAppInfo * > & List,AppsCategories Type) const53a9b1e059SJoachim Henze CAppDB::GetApps(CAtlList<CAppInfo *> &List, AppsCategories Type) const
54a9b1e059SJoachim Henze {
55a9b1e059SJoachim Henze const BOOL UseInstalled = IsInstalledEnum(Type);
56a9b1e059SJoachim Henze const CAtlList<CAppInfo *> &list = UseInstalled ? m_Installed : m_Available;
57a9b1e059SJoachim Henze const BOOL IncludeAll = UseInstalled ? (Type == ENUM_ALL_INSTALLED) : (Type == ENUM_ALL_AVAILABLE);
58a9b1e059SJoachim Henze
59a9b1e059SJoachim Henze POSITION CurrentListPosition = list.GetHeadPosition();
60a9b1e059SJoachim Henze while (CurrentListPosition)
61a9b1e059SJoachim Henze {
62a9b1e059SJoachim Henze CAppInfo *Info = list.GetNext(CurrentListPosition);
63a9b1e059SJoachim Henze
64a9b1e059SJoachim Henze if (IncludeAll || Type == Info->iCategory)
65a9b1e059SJoachim Henze {
66a9b1e059SJoachim Henze List.AddTail(Info);
67a9b1e059SJoachim Henze }
68a9b1e059SJoachim Henze }
69a9b1e059SJoachim Henze }
70a9b1e059SJoachim Henze
71a9b1e059SJoachim Henze BOOL
EnumerateFiles()72a9b1e059SJoachim Henze CAppDB::EnumerateFiles()
73a9b1e059SJoachim Henze {
74a9b1e059SJoachim Henze ClearList(m_Available);
75a9b1e059SJoachim Henze
76a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath;
77fefc5f48SHermès Bélusca-Maïto AppsPath += RAPPS_DATABASE_SUBDIR;
78a9b1e059SJoachim Henze CPathW WildcardPath = AppsPath;
79a9b1e059SJoachim Henze WildcardPath += L"*.txt";
80a9b1e059SJoachim Henze
81a9b1e059SJoachim Henze WIN32_FIND_DATAW FindFileData;
82a9b1e059SJoachim Henze HANDLE hFind = FindFirstFileW(WildcardPath, &FindFileData);
83a9b1e059SJoachim Henze if (hFind == INVALID_HANDLE_VALUE)
84a9b1e059SJoachim Henze {
85a9b1e059SJoachim Henze return FALSE;
86a9b1e059SJoachim Henze }
87a9b1e059SJoachim Henze
88a9b1e059SJoachim Henze do
89a9b1e059SJoachim Henze {
90a9b1e059SJoachim Henze CStringW szPkgName = FindFileData.cFileName;
91a9b1e059SJoachim Henze PathRemoveExtensionW(szPkgName.GetBuffer(MAX_PATH));
92a9b1e059SJoachim Henze szPkgName.ReleaseBuffer();
93a9b1e059SJoachim Henze
94a9b1e059SJoachim Henze CAppInfo *Info = FindByPackageName(szPkgName);
95a9b1e059SJoachim Henze ATLASSERT(Info == NULL);
96a9b1e059SJoachim Henze if (!Info)
97a9b1e059SJoachim Henze {
98fefc5f48SHermès Bélusca-Maïto CConfigParser *Parser = new CConfigParser(CPathW(AppsPath) += FindFileData.cFileName);
99a9b1e059SJoachim Henze int Cat;
100*57b775efSWhindmar Saksit if (!Parser->GetInt(DB_CATEGORY, Cat))
101a9b1e059SJoachim Henze Cat = ENUM_INVALID;
102a9b1e059SJoachim Henze
103a9b1e059SJoachim Henze Info = new CAvailableApplicationInfo(Parser, szPkgName, static_cast<AppsCategories>(Cat), AppsPath);
104a9b1e059SJoachim Henze if (Info->Valid())
105a9b1e059SJoachim Henze {
106a9b1e059SJoachim Henze m_Available.AddTail(Info);
107a9b1e059SJoachim Henze }
108a9b1e059SJoachim Henze else
109a9b1e059SJoachim Henze {
110a9b1e059SJoachim Henze delete Info;
111a9b1e059SJoachim Henze }
112a9b1e059SJoachim Henze }
113a9b1e059SJoachim Henze
114a9b1e059SJoachim Henze } while (FindNextFileW(hFind, &FindFileData));
115a9b1e059SJoachim Henze
116a9b1e059SJoachim Henze FindClose(hFind);
117a9b1e059SJoachim Henze return TRUE;
118a9b1e059SJoachim Henze }
119a9b1e059SJoachim Henze
120a9b1e059SJoachim Henze VOID
UpdateAvailable()121a9b1e059SJoachim Henze CAppDB::UpdateAvailable()
122a9b1e059SJoachim Henze {
123a9b1e059SJoachim Henze if (!CreateDirectoryW(m_BasePath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
124a9b1e059SJoachim Henze return;
125a9b1e059SJoachim Henze
126a9b1e059SJoachim Henze if (EnumerateFiles())
127a9b1e059SJoachim Henze return;
128a9b1e059SJoachim Henze
129a9b1e059SJoachim Henze DownloadApplicationsDB(
130a9b1e059SJoachim Henze SettingsInfo.bUseSource ? SettingsInfo.szSourceURL : APPLICATION_DATABASE_URL, !SettingsInfo.bUseSource);
131a9b1e059SJoachim Henze
132a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath;
133fefc5f48SHermès Bélusca-Maïto AppsPath += RAPPS_DATABASE_SUBDIR;
134a9b1e059SJoachim Henze if (!ExtractFilesFromCab(APPLICATION_DATABASE_NAME, m_BasePath, AppsPath))
135a9b1e059SJoachim Henze return;
136a9b1e059SJoachim Henze
137a9b1e059SJoachim Henze CPathW CabFile = m_BasePath;
138a9b1e059SJoachim Henze CabFile += APPLICATION_DATABASE_NAME;
139a9b1e059SJoachim Henze DeleteFileW(CabFile);
140a9b1e059SJoachim Henze
141a9b1e059SJoachim Henze EnumerateFiles();
142a9b1e059SJoachim Henze }
143a9b1e059SJoachim Henze
144*57b775efSWhindmar Saksit static inline HKEY
GetRootKeyInfo(UINT Index,REGSAM & RegSam)145*57b775efSWhindmar Saksit GetRootKeyInfo(UINT Index, REGSAM &RegSam)
146*57b775efSWhindmar Saksit {
147*57b775efSWhindmar Saksit C_ASSERT(_countof(g_RootKeyEnum) == _countof(g_RegSamEnum));
148*57b775efSWhindmar Saksit if (Index < _countof(g_RootKeyEnum))
149*57b775efSWhindmar Saksit {
150*57b775efSWhindmar Saksit RegSam = g_RegSamEnum[Index];
151*57b775efSWhindmar Saksit return g_RootKeyEnum[Index];
152*57b775efSWhindmar Saksit }
153*57b775efSWhindmar Saksit return NULL;
154*57b775efSWhindmar Saksit }
155*57b775efSWhindmar Saksit
156*57b775efSWhindmar Saksit HKEY
EnumInstalledRootKey(UINT Index,REGSAM & RegSam)157*57b775efSWhindmar Saksit CAppDB::EnumInstalledRootKey(UINT Index, REGSAM &RegSam)
158*57b775efSWhindmar Saksit {
159*57b775efSWhindmar Saksit // Loop for through all combinations.
160*57b775efSWhindmar Saksit // Note that HKEY_CURRENT_USER\Software does not have a redirect
161*57b775efSWhindmar Saksit // https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys#redirected-shared-and-reflected-keys-under-wow64
162*57b775efSWhindmar Saksit if (Index < (IsSystem64Bit() ? 3 : 2))
163*57b775efSWhindmar Saksit return GetRootKeyInfo(Index, RegSam);
164*57b775efSWhindmar Saksit else
165*57b775efSWhindmar Saksit return NULL;
166*57b775efSWhindmar Saksit }
167*57b775efSWhindmar Saksit
168*57b775efSWhindmar Saksit CInstalledApplicationInfo *
CreateInstalledAppByRegistryKey(LPCWSTR KeyName,HKEY hKeyParent,UINT KeyIndex)169*57b775efSWhindmar Saksit CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR KeyName, HKEY hKeyParent, UINT KeyIndex)
170*57b775efSWhindmar Saksit {
171*57b775efSWhindmar Saksit CRegKey hSubKey;
172*57b775efSWhindmar Saksit if (hSubKey.Open(hKeyParent, KeyName, KEY_READ) != ERROR_SUCCESS)
173*57b775efSWhindmar Saksit return NULL;
174*57b775efSWhindmar Saksit DWORD value, size;
175*57b775efSWhindmar Saksit
176*57b775efSWhindmar Saksit size = sizeof(DWORD);
177*57b775efSWhindmar Saksit if (!RegQueryValueExW(hSubKey, L"SystemComponent", NULL, NULL, (LPBYTE)&value, &size) && value == 1)
178*57b775efSWhindmar Saksit {
179*57b775efSWhindmar Saksit // Ignore system components
180*57b775efSWhindmar Saksit return NULL;
181*57b775efSWhindmar Saksit }
182*57b775efSWhindmar Saksit
183*57b775efSWhindmar Saksit size = 0;
184*57b775efSWhindmar Saksit BOOL bIsUpdate = !RegQueryValueExW(hSubKey, L"ParentKeyName", NULL, NULL, NULL, &size);
185*57b775efSWhindmar Saksit
186*57b775efSWhindmar Saksit AppsCategories cat = bIsUpdate ? ENUM_UPDATES : ENUM_INSTALLED_APPLICATIONS;
187*57b775efSWhindmar Saksit CInstalledApplicationInfo *pInfo;
188*57b775efSWhindmar Saksit pInfo = new CInstalledApplicationInfo(hSubKey.Detach(), KeyName, cat, KeyIndex);
189*57b775efSWhindmar Saksit if (pInfo && pInfo->Valid())
190*57b775efSWhindmar Saksit {
191*57b775efSWhindmar Saksit return pInfo;
192*57b775efSWhindmar Saksit }
193*57b775efSWhindmar Saksit delete pInfo;
194*57b775efSWhindmar Saksit return NULL;
195*57b775efSWhindmar Saksit }
196*57b775efSWhindmar Saksit
197*57b775efSWhindmar Saksit CInstalledApplicationInfo *
EnumerateRegistry(CAtlList<CAppInfo * > * List,LPCWSTR SearchOnly)198*57b775efSWhindmar Saksit CAppDB::EnumerateRegistry(CAtlList<CAppInfo *> *List, LPCWSTR SearchOnly)
199*57b775efSWhindmar Saksit {
200*57b775efSWhindmar Saksit ATLASSERT(List || SearchOnly);
201*57b775efSWhindmar Saksit REGSAM wowsam;
202*57b775efSWhindmar Saksit HKEY hRootKey;
203*57b775efSWhindmar Saksit for (UINT rki = 0; (hRootKey = EnumInstalledRootKey(rki, wowsam)); ++rki)
204*57b775efSWhindmar Saksit {
205*57b775efSWhindmar Saksit CRegKey hKey;
206*57b775efSWhindmar Saksit if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | wowsam) != ERROR_SUCCESS)
207*57b775efSWhindmar Saksit {
208*57b775efSWhindmar Saksit continue;
209*57b775efSWhindmar Saksit }
210*57b775efSWhindmar Saksit for (DWORD Index = 0;; ++Index)
211*57b775efSWhindmar Saksit {
212*57b775efSWhindmar Saksit WCHAR szKeyName[MAX_PATH];
213*57b775efSWhindmar Saksit DWORD dwSize = _countof(szKeyName);
214*57b775efSWhindmar Saksit if (hKey.EnumKey(Index, szKeyName, &dwSize) != ERROR_SUCCESS)
215*57b775efSWhindmar Saksit {
216*57b775efSWhindmar Saksit break;
217*57b775efSWhindmar Saksit }
218*57b775efSWhindmar Saksit if (List || !StrCmpIW(SearchOnly, szKeyName))
219*57b775efSWhindmar Saksit {
220*57b775efSWhindmar Saksit CInstalledApplicationInfo *Info;
221*57b775efSWhindmar Saksit Info = CreateInstalledAppByRegistryKey(szKeyName, hKey, rki);
222*57b775efSWhindmar Saksit if (Info)
223*57b775efSWhindmar Saksit {
224*57b775efSWhindmar Saksit if (List)
225*57b775efSWhindmar Saksit List->AddTail(Info);
226*57b775efSWhindmar Saksit else
227*57b775efSWhindmar Saksit return Info;
228*57b775efSWhindmar Saksit }
229*57b775efSWhindmar Saksit }
230*57b775efSWhindmar Saksit }
231*57b775efSWhindmar Saksit }
232*57b775efSWhindmar Saksit return NULL;
233*57b775efSWhindmar Saksit }
234*57b775efSWhindmar Saksit
235a9b1e059SJoachim Henze VOID
UpdateInstalled()236a9b1e059SJoachim Henze CAppDB::UpdateInstalled()
237a9b1e059SJoachim Henze {
238a9b1e059SJoachim Henze // Remove all old entries
239a9b1e059SJoachim Henze ClearList(m_Installed);
240a9b1e059SJoachim Henze
241*57b775efSWhindmar Saksit EnumerateRegistry(&m_Installed, NULL);
242a9b1e059SJoachim Henze }
243a9b1e059SJoachim Henze
244*57b775efSWhindmar Saksit CInstalledApplicationInfo *
CreateInstalledAppByRegistryKey(LPCWSTR Name)245*57b775efSWhindmar Saksit CAppDB::CreateInstalledAppByRegistryKey(LPCWSTR Name)
246a9b1e059SJoachim Henze {
247*57b775efSWhindmar Saksit return EnumerateRegistry(NULL, Name);
248*57b775efSWhindmar Saksit }
249a9b1e059SJoachim Henze
250*57b775efSWhindmar Saksit CInstalledApplicationInfo *
CreateInstalledAppInstance(LPCWSTR KeyName,BOOL User,REGSAM WowSam)251*57b775efSWhindmar Saksit CAppDB::CreateInstalledAppInstance(LPCWSTR KeyName, BOOL User, REGSAM WowSam)
252*57b775efSWhindmar Saksit {
253*57b775efSWhindmar Saksit HKEY hRootKey = User ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
254*57b775efSWhindmar Saksit UINT KeyIndex = User ? (0) : ((WowSam & KEY_WOW64_64KEY) ? 2 : 1);
255a9b1e059SJoachim Henze CRegKey hKey;
256*57b775efSWhindmar Saksit if (hKey.Open(hRootKey, UNINSTALL_SUBKEY, KEY_READ | WowSam) == ERROR_SUCCESS)
257a9b1e059SJoachim Henze {
258*57b775efSWhindmar Saksit return CreateInstalledAppByRegistryKey(KeyName, hKey, KeyIndex);
259a9b1e059SJoachim Henze }
260*57b775efSWhindmar Saksit return NULL;
261a9b1e059SJoachim Henze }
262a9b1e059SJoachim Henze
263a9b1e059SJoachim Henze static void
DeleteWithWildcard(const CPathW & Dir,const CStringW & Filter)264a9b1e059SJoachim Henze DeleteWithWildcard(const CPathW &Dir, const CStringW &Filter)
265a9b1e059SJoachim Henze {
266a9b1e059SJoachim Henze HANDLE hFind = INVALID_HANDLE_VALUE;
267a9b1e059SJoachim Henze WIN32_FIND_DATAW FindFileData;
268a9b1e059SJoachim Henze
269a9b1e059SJoachim Henze CPathW DirWithFilter = Dir;
270a9b1e059SJoachim Henze DirWithFilter += Filter;
271a9b1e059SJoachim Henze
272a9b1e059SJoachim Henze hFind = FindFirstFileW(DirWithFilter, &FindFileData);
273a9b1e059SJoachim Henze
274a9b1e059SJoachim Henze if (hFind == INVALID_HANDLE_VALUE)
275a9b1e059SJoachim Henze return;
276a9b1e059SJoachim Henze
277a9b1e059SJoachim Henze do
278a9b1e059SJoachim Henze {
279a9b1e059SJoachim Henze CPathW szTmp = Dir;
280a9b1e059SJoachim Henze szTmp += FindFileData.cFileName;
281a9b1e059SJoachim Henze
282a9b1e059SJoachim Henze if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
283a9b1e059SJoachim Henze {
284a9b1e059SJoachim Henze DeleteFileW(szTmp);
285a9b1e059SJoachim Henze }
286a9b1e059SJoachim Henze } while (FindNextFileW(hFind, &FindFileData) != 0);
287a9b1e059SJoachim Henze FindClose(hFind);
288a9b1e059SJoachim Henze }
289a9b1e059SJoachim Henze
290a9b1e059SJoachim Henze VOID
RemoveCached()291a9b1e059SJoachim Henze CAppDB::RemoveCached()
292a9b1e059SJoachim Henze {
293a9b1e059SJoachim Henze // Delete icons
294a9b1e059SJoachim Henze CPathW AppsPath = m_BasePath;
295fefc5f48SHermès Bélusca-Maïto AppsPath += RAPPS_DATABASE_SUBDIR;
296a9b1e059SJoachim Henze CPathW IconPath = AppsPath;
297a9b1e059SJoachim Henze IconPath += L"icons";
298a9b1e059SJoachim Henze DeleteWithWildcard(IconPath, L"*.ico");
299a9b1e059SJoachim Henze
300a9b1e059SJoachim Henze // Delete leftover screenshots
301a9b1e059SJoachim Henze CPathW ScrnshotFolder = AppsPath;
302a9b1e059SJoachim Henze ScrnshotFolder += L"screenshots";
303a9b1e059SJoachim Henze DeleteWithWildcard(ScrnshotFolder, L"*.tmp");
304a9b1e059SJoachim Henze
305a9b1e059SJoachim Henze // Delete data base files (*.txt)
306a9b1e059SJoachim Henze DeleteWithWildcard(AppsPath, L"*.txt");
307a9b1e059SJoachim Henze
308a9b1e059SJoachim Henze RemoveDirectoryW(IconPath);
309a9b1e059SJoachim Henze RemoveDirectoryW(ScrnshotFolder);
310a9b1e059SJoachim Henze RemoveDirectoryW(AppsPath);
311a9b1e059SJoachim Henze RemoveDirectoryW(m_BasePath);
312a9b1e059SJoachim Henze }
313a9b1e059SJoachim Henze
314*57b775efSWhindmar Saksit DWORD
RemoveInstalledAppFromRegistry(const CAppInfo * Info)315a9b1e059SJoachim Henze CAppDB::RemoveInstalledAppFromRegistry(const CAppInfo *Info)
316a9b1e059SJoachim Henze {
317a9b1e059SJoachim Henze // Validate that this is actually an installed app / update
318a9b1e059SJoachim Henze ATLASSERT(Info->iCategory == ENUM_INSTALLED_APPLICATIONS || Info->iCategory == ENUM_UPDATES);
319a9b1e059SJoachim Henze if (Info->iCategory != ENUM_INSTALLED_APPLICATIONS && Info->iCategory != ENUM_UPDATES)
320*57b775efSWhindmar Saksit return ERROR_INVALID_PARAMETER;
321a9b1e059SJoachim Henze
322a9b1e059SJoachim Henze const CInstalledApplicationInfo *InstalledInfo = static_cast<const CInstalledApplicationInfo *>(Info);
323a9b1e059SJoachim Henze
324a9b1e059SJoachim Henze CStringW Name = InstalledInfo->szIdentifier;
325*57b775efSWhindmar Saksit REGSAM wowsam;
326*57b775efSWhindmar Saksit HKEY hRoot = GetRootKeyInfo(InstalledInfo->m_KeyInfo, wowsam);
327*57b775efSWhindmar Saksit ATLASSERT(hRoot);
328*57b775efSWhindmar Saksit if (!hRoot)
329*57b775efSWhindmar Saksit return ERROR_OPEN_FAILED;
330a9b1e059SJoachim Henze
331a9b1e059SJoachim Henze CRegKey Uninstall;
332*57b775efSWhindmar Saksit LSTATUS err = Uninstall.Open(hRoot, UNINSTALL_SUBKEY, KEY_READ | KEY_WRITE | wowsam);
333*57b775efSWhindmar Saksit if (err == ERROR_SUCCESS)
334*57b775efSWhindmar Saksit {
335*57b775efSWhindmar Saksit err = Uninstall.RecurseDeleteKey(Name);
336*57b775efSWhindmar Saksit }
337*57b775efSWhindmar Saksit return err;
338a9b1e059SJoachim Henze }
339