[BOOTDATA]
[reactos.git] / reactos / dll / appcompat / apphelp / hsdb.c
1 /*
2 * Copyright 2011 André Hentschel
3 * Copyright 2013 Mislav Blažević
4 * Copyright 2015,2016 Mark Jansen
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #define WIN32_NO_STATUS
22 #include "windows.h"
23 #include "ntndk.h"
24 #include "strsafe.h"
25 #include "apphelp.h"
26
27 #include "wine/unicode.h"
28
29
30 static BOOL WINAPI SdbpFileExists(LPCWSTR path)
31 {
32 DWORD attr = GetFileAttributesW(path);
33 return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY));
34 }
35
36
37 /**
38 * Opens specified shim database file Handle returned by this function may only be used by
39 * functions which take HSDB param thus differing it from SdbOpenDatabase.
40 *
41 * @param [in] flags Specifies type of path or predefined database.
42 * @param [in] path Path to the shim database file.
43 *
44 * @return Success: Handle to the opened shim database, NULL otherwise.
45 */
46 HSDB WINAPI SdbInitDatabase(DWORD flags, LPCWSTR path)
47 {
48 static const WCHAR shim[] = {'\\','s','y','s','m','a','i','n','.','s','d','b',0};
49 static const WCHAR msi[] = {'\\','m','s','i','m','a','i','n','.','s','d','b',0};
50 static const WCHAR drivers[] = {'\\','d','r','v','m','a','i','n','.','s','d','b',0};
51 LPCWSTR name;
52 WCHAR buffer[128];
53 HSDB sdb;
54
55 sdb = SdbAlloc(sizeof(SDB));
56 if (!sdb)
57 return NULL;
58 sdb->auto_loaded = 0;
59
60 /* Check for predefined databases */
61 if ((flags & HID_DATABASE_TYPE_MASK) && path == NULL)
62 {
63 switch (flags & HID_DATABASE_TYPE_MASK)
64 {
65 case SDB_DATABASE_MAIN_SHIM: name = shim; break;
66 case SDB_DATABASE_MAIN_MSI: name = msi; break;
67 case SDB_DATABASE_MAIN_DRIVERS: name = drivers; break;
68 default:
69 SdbReleaseDatabase(sdb);
70 return NULL;
71 }
72 SdbGetAppPatchDir(NULL, buffer, 128);
73 memcpy(buffer + lstrlenW(buffer), name, SdbpStrlen(name));
74 }
75
76 sdb->db = SdbOpenDatabase(path ? path : buffer, (flags & 0xF) - 1);
77
78 /* If database could not be loaded, a handle doesn't make sense either */
79 if (!sdb->db)
80 {
81 SdbReleaseDatabase(sdb);
82 return NULL;
83 }
84
85 return sdb;
86 }
87
88 /**
89 * Closes shim database opened by SdbInitDatabase.
90 *
91 * @param [in] hsdb Handle to the shim database.
92 */
93 void WINAPI SdbReleaseDatabase(HSDB hsdb)
94 {
95 SdbCloseDatabase(hsdb->db);
96 SdbFree(hsdb);
97 }
98
99 /**
100 * Queries database for a specified exe If hsdb is NULL default database shall be loaded and
101 * searched.
102 *
103 * @param [in] hsdb Handle to the shim database.
104 * @param [in] path Path to executable for which we query database.
105 * @param [in] module_name Unused.
106 * @param [in] env Unused.
107 * @param [in] flags 0 or SDBGMEF_IGNORE_ENVIRONMENT.
108 * @param [out] result Pointer to structure in which query result shall be stored.
109 *
110 * @return TRUE if it succeeds, FALSE if it fails.
111 */
112 BOOL WINAPI SdbGetMatchingExe(HSDB hsdb, LPCWSTR path, LPCWSTR module_name,
113 LPCWSTR env, DWORD flags, PSDBQUERYRESULT result)
114 {
115 static const WCHAR fmt[] = {'%','s','%','s',0};
116 BOOL ok, ret;
117 TAGID database, iter, attr;
118 PATTRINFO attribs = NULL;
119 /*DWORD attr_count;*/
120 LPWSTR file_name;
121 WCHAR dir_path[128];
122 WCHAR buffer[256];
123 PDB db;
124
125 /* Load default database if one is not specified */
126 if (!hsdb)
127 {
128 /* To reproduce windows behaviour HID_DOS_PATHS needs
129 * to be specified when loading default database */
130 hsdb = SdbInitDatabase(HID_DOS_PATHS | SDB_DATABASE_MAIN_SHIM, NULL);
131 if(hsdb)
132 hsdb->auto_loaded = TRUE;
133 }
134
135 /* No database could be loaded */
136 if (!hsdb)
137 return FALSE;
138
139 db = hsdb->db;
140
141 /* Extract file name */
142 file_name = strrchrW(path, '\\') + 1;
143
144 /* Extract directory path */
145 memcpy(dir_path, path, (size_t)(file_name - path) * sizeof(WCHAR));
146
147 /* Get information about executable required to match it with database entry */
148 /*if (!SdbGetFileAttributes(path, &attribs, &attr_count))
149 return FALSE;*/
150
151 /* DATABASE is list TAG which contains all executables */
152 database = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
153 if (database == TAGID_NULL)
154 return FALSE;
155
156 /* EXE is list TAG which contains data required to match executable */
157 iter = SdbFindFirstTag(db, database, TAG_EXE);
158
159 /* Search for entry in database */
160 while (iter != TAGID_NULL)
161 {
162 /* Check if exe name matches */
163 attr = SdbFindFirstTag(db, iter, TAG_NAME);
164 if (lstrcmpiW(SdbGetStringTagPtr(db, attr), file_name) == 0)
165 {
166 /* Assume that entry is found (in case there are no "matching files") */
167 ok = TRUE;
168
169 /* Check if all "matching files" exist */
170 /* TODO: check size/checksum as well */
171 for (attr = SdbFindFirstTag(db, attr, TAG_MATCHING_FILE);
172 attr != TAGID_NULL; attr = SdbFindNextTag(db, iter, attr))
173 {
174 snprintfW(buffer, 256, fmt, dir_path, SdbGetStringTagPtr(db, attr));
175 if (!SdbpFileExists(buffer))
176 ok = FALSE;
177 }
178
179 /* Found it! */
180 if (ok)
181 {
182 /* TODO: fill result data */
183 /* TODO: there may be multiple matches */
184 ret = TRUE;
185 goto cleanup;
186 }
187 }
188
189 /* Continue iterating */
190 iter = SdbFindNextTag(db, database, iter);
191 }
192
193 /* Exe not found */
194 ret = FALSE;
195
196 cleanup:
197 SdbFreeFileAttributes(attribs);
198 if (hsdb->auto_loaded) SdbReleaseDatabase(hsdb);
199 return ret;
200 }
201
202 /**
203 * Retrieves AppPatch directory.
204 *
205 * @param [in] db Handle to the shim database.
206 * @param [out] path Pointer to memory in which path shall be written.
207 * @param [in] size Size of the buffer in characters.
208 */
209 BOOL WINAPI SdbGetAppPatchDir(HSDB db, LPWSTR path, DWORD size)
210 {
211 static WCHAR* default_dir = NULL;
212 static CONST WCHAR szAppPatch[] = {'\\','A','p','p','P','a','t','c','h',0};
213
214 /* In case function fails, path holds empty string */
215 if (size > 0)
216 *path = 0;
217
218 if (!default_dir)
219 {
220 WCHAR* tmp = NULL;
221 UINT len = GetSystemWindowsDirectoryW(NULL, 0) + lstrlenW(szAppPatch);
222 tmp = SdbAlloc((len + 1)* sizeof(WCHAR));
223 if (tmp)
224 {
225 UINT r = GetSystemWindowsDirectoryW(tmp, len+1);
226 if (r && r < len)
227 {
228 if (SUCCEEDED(StringCchCatW(tmp, len+1, szAppPatch)))
229 {
230 if (InterlockedCompareExchangePointer((void**)&default_dir, tmp, NULL) == NULL)
231 tmp = NULL;
232 }
233 }
234 if (tmp)
235 SdbFree(tmp);
236 }
237 if (!default_dir)
238 {
239 SHIM_ERR("Unable to obtain default AppPatch directory\n");
240 return FALSE;
241 }
242 }
243
244 if (!db)
245 {
246 return SUCCEEDED(StringCchCopyW(path, size, default_dir));
247 }
248 else
249 {
250 SHIM_ERR("Unimplemented for db != NULL\n");
251 return FALSE;
252 }
253 }
254
255
256 /**
257 * Translates the given trWhich to a specific database / tagid
258 *
259 * @param [in] hsdb Handle to the database.
260 * @param [in] trWhich Tagref to find
261 * @param [out,opt] ppdb The Shim database that trWhich belongs to.
262 * @param [out,opt] ptiWhich The tagid that trWhich corresponds to.
263 *
264 * @return TRUE if it succeeds, FALSE if it fails.
265 */
266 BOOL WINAPI SdbTagRefToTagID(HSDB hsdb, TAGREF trWhich, PDB* ppdb, TAGID* ptiWhich)
267 {
268 if (trWhich & 0xf0000000)
269 {
270 SHIM_ERR("Multiple shim databases not yet implemented!\n");
271 if (ppdb)
272 *ppdb = NULL;
273 if (ptiWhich)
274 *ptiWhich = TAG_NULL;
275 return FALSE;
276 }
277
278 /* There seems to be no range checking on trWhich.. */
279 if (ppdb)
280 *ppdb = hsdb->db;
281 if (ptiWhich)
282 *ptiWhich = trWhich & 0x0fffffff;
283
284 return TRUE;
285 }
286
287 /**
288 * Translates the given trWhich to a specific database / tagid
289 *
290 * @param [in] hsdb Handle to the database.
291 * @param [in] pdb The Shim database that tiWhich belongs to.
292 * @param [in] tiWhich Path to executable for which we query database.
293 * @param [out,opt] ptrWhich The tagid that tiWhich corresponds to.
294 *
295 * @return TRUE if it succeeds, FALSE if it fails.
296 */
297 BOOL WINAPI SdbTagIDToTagRef(HSDB hsdb, PDB pdb, TAGID tiWhich, TAGREF* ptrWhich)
298 {
299 if (pdb != hsdb->db)
300 {
301 SHIM_ERR("Multiple shim databases not yet implemented!\n");
302 if (ptrWhich)
303 *ptrWhich = TAGREF_NULL;
304 return FALSE;
305 }
306
307 if (ptrWhich)
308 *ptrWhich = tiWhich & 0x0fffffff;
309
310 return TRUE;
311 }
312
313
314