[APPHELP][APPHELP_APITEST] Update db apitests to succeed from 2k3 to 10, paving the...
[reactos.git] / reactos / dll / appcompat / apphelp / sdbfileattr.c
1 /*
2 * Copyright 2011 André Hentschel
3 * Copyright 2013 Mislav Bla\9eevic
4 * Copyright 2015 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 "windef.h"
23 #include "winbase.h"
24 #include "apphelp.h"
25 #include "imagehlp.h"
26 #include "winver.h"
27 #include "rtlfuncs.h"
28
29 #include "wine/unicode.h"
30
31 #define NUM_ATTRIBUTES 28
32 enum APPHELP_MODULETYPE
33 {
34 MODTYPE_UNKNOWN = 0,
35 MODTYPE_DOS = 1,
36 MODTYPE_NE = 2,
37 MODTYPE_PE = 3,
38 };
39
40
41 static void WINAPI SdbpSetDWORDAttr(PATTRINFO attr, TAG tag, DWORD value)
42 {
43 attr->type = tag;
44 attr->flags = ATTRIBUTE_AVAILABLE;
45 attr->dwattr = value;
46 }
47
48 static void WINAPI SdbpSetQWORDAttr(PATTRINFO attr, TAG tag, QWORD value)
49 {
50 attr->type = tag;
51 attr->flags = ATTRIBUTE_AVAILABLE;
52 attr->qwattr = value;
53 }
54
55 static void WINAPI SdbpSetStringAttr(PATTRINFO attr, TAG tag, WCHAR *string)
56 {
57 if (!string)
58 {
59 attr->flags = ATTRIBUTE_FAILED;
60 return;
61 }
62
63 attr->type = tag;
64 attr->flags = ATTRIBUTE_AVAILABLE;
65 attr->lpattr = SdbpStrDup(string);
66 }
67
68 static void WINAPI SdbpSetAttrFail(PATTRINFO attr)
69 {
70 attr->flags = ATTRIBUTE_FAILED;
71 }
72
73 static WCHAR* WINAPI SdbpGetStringAttr(LPWSTR translation, LPCWSTR attr, PVOID file_info)
74 {
75 UINT size = 0;
76 PVOID buffer;
77 WCHAR value[128] = {0};
78
79 if (!file_info)
80 return NULL;
81
82 snprintfW(value, 128, translation, attr);
83 if (VerQueryValueW(file_info, value, &buffer, &size) && size != 0)
84 return (WCHAR*)buffer;
85
86 return NULL;
87 }
88
89 static void WINAPI SdbpSetStringAttrFromAnsiString(PATTRINFO attr, TAG tag, PBYTE string, BYTE len)
90 {
91 WCHAR* dest;
92 if (!string)
93 {
94 attr->flags = ATTRIBUTE_FAILED;
95 return;
96 }
97
98 attr->type = tag;
99 attr->flags = ATTRIBUTE_AVAILABLE;
100 dest = attr->lpattr = SdbpAlloc((len+1) * sizeof(WCHAR));
101 while (len--)
102 *(dest++) = *(string++);
103 *dest = 0;
104 }
105
106 static void WINAPI SdbpSetStringAttrFromPascalString(PATTRINFO attr, TAG tag, PBYTE string)
107 {
108 if (!string)
109 {
110 attr->flags = ATTRIBUTE_FAILED;
111 return;
112 }
113
114 SdbpSetStringAttrFromAnsiString(attr, tag, string + 1, *string);
115 }
116
117 static void SdbpReadFileVersion(PATTRINFO attr_info, PVOID file_info)
118 {
119 static const WCHAR str_root[] = {'\\',0};
120
121 VS_FIXEDFILEINFO* fixed_info;
122 UINT size;
123 if (file_info && VerQueryValueW(file_info, str_root, (LPVOID*)&fixed_info, &size) && size)
124 {
125 if (fixed_info->dwSignature == VS_FFI_SIGNATURE)
126 {
127 LARGE_INTEGER version;
128 version.HighPart = fixed_info->dwFileVersionMS;
129 version.LowPart = fixed_info->dwFileVersionLS;
130 SdbpSetQWORDAttr(&attr_info[2], TAG_BIN_FILE_VERSION, version.QuadPart);
131 SdbpSetQWORDAttr(&attr_info[21], TAG_UPTO_BIN_FILE_VERSION, version.QuadPart);
132 version.HighPart = fixed_info->dwProductVersionMS;
133 version.LowPart = fixed_info->dwProductVersionLS;
134 SdbpSetQWORDAttr(&attr_info[3], TAG_BIN_PRODUCT_VERSION, version.QuadPart);
135 SdbpSetQWORDAttr(&attr_info[22], TAG_UPTO_BIN_PRODUCT_VERSION, version.QuadPart);
136
137 SdbpSetDWORDAttr(&attr_info[12], TAG_VERDATEHI, fixed_info->dwFileDateMS);
138 SdbpSetDWORDAttr(&attr_info[13], TAG_VERDATELO, fixed_info->dwFileDateLS);
139 SdbpSetDWORDAttr(&attr_info[14], TAG_VERFILEOS, fixed_info->dwFileOS); /* 0x000, 0x4, 0x40004, 0x40000, 0x10004, 0x10001*/
140 SdbpSetDWORDAttr(&attr_info[15], TAG_VERFILETYPE, fixed_info->dwFileType); /* VFT_APP, VFT_DLL, .... */
141 return;
142 }
143 }
144
145 SdbpSetAttrFail(&attr_info[2]);
146 SdbpSetAttrFail(&attr_info[3]);
147 SdbpSetAttrFail(&attr_info[12]);
148 SdbpSetAttrFail(&attr_info[13]);
149 SdbpSetAttrFail(&attr_info[14]);
150 SdbpSetAttrFail(&attr_info[15]);
151 SdbpSetAttrFail(&attr_info[21]);
152 SdbpSetAttrFail(&attr_info[22]);
153 }
154
155 static DWORD WINAPI SdbpCalculateFileChecksum(PMEMMAPPED mapping)
156 {
157 size_t n, size;
158 PDWORD data;
159 DWORD checks = 0, carry = 0;
160
161 if (mapping->size < 4)
162 return 0;
163
164 if (mapping->size >= 0x1000)
165 {
166 size = 0x1000;
167 if (mapping->size < 0x1200)
168 data = (PDWORD)(mapping->view + mapping->size - size);
169 else
170 data = (PDWORD)mapping->view + (0x200 / 4);
171 }
172 else
173 {
174 data = (PDWORD)mapping->view;
175 size = mapping->size;
176 }
177
178 for (n = 0; n < size / 4; ++n)
179 {
180 checks += *data;
181 carry = (checks & 1) ? 0x80000000 : 0;
182 checks >>= 1;
183 checks |= carry;
184 ++data;
185 }
186 return checks;
187 }
188
189 static DWORD WINAPI SdbpGetModuleType(PMEMMAPPED mapping, PIMAGE_NT_HEADERS* nt_headers)
190 {
191 PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mapping->view;
192 PIMAGE_OS2_HEADER os2;
193
194 *nt_headers = NULL;
195
196 if (mapping->size < 2 || dos->e_magic != IMAGE_DOS_SIGNATURE)
197 return MODTYPE_UNKNOWN;
198
199 if (mapping->size < sizeof(IMAGE_DOS_HEADER) || mapping->size < (dos->e_lfanew+2))
200 return MODTYPE_DOS;
201
202 os2 = (PIMAGE_OS2_HEADER)((PBYTE)dos + dos->e_lfanew);
203 if (os2->ne_magic == IMAGE_OS2_SIGNATURE || os2->ne_magic == IMAGE_OS2_SIGNATURE_LE)
204 {
205 *nt_headers = (PIMAGE_NT_HEADERS)os2;
206 return MODTYPE_NE;
207 }
208
209 if (mapping->size >= (dos->e_lfanew + 4) && ((PIMAGE_NT_HEADERS)os2)->Signature == IMAGE_NT_SIGNATURE)
210 {
211 *nt_headers = (PIMAGE_NT_HEADERS)os2;
212 return MODTYPE_PE;
213 }
214
215 return MODTYPE_DOS;
216 }
217
218 /**
219 * Frees attribute data allocated by SdbGetFileAttributes.
220 *
221 * @note Unlike Windows, this implementation will not crash if attr_info is NULL.
222 *
223 * @param [in] attr_info Pointer to array of ATTRINFO which will be freed.
224 *
225 * @return TRUE if it succeeds, FALSE if it fails.
226 */
227 BOOL WINAPI SdbFreeFileAttributes(PATTRINFO attr_info)
228 {
229 WORD i;
230
231 if (!attr_info)
232 return FALSE;
233
234 for (i = 0; i < NUM_ATTRIBUTES; i++)
235 if ((attr_info[i].type & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
236 SdbFree(attr_info[i].lpattr);
237 SdbFree(attr_info);
238 return TRUE;
239 }
240
241 /**
242 * Retrieves attribute data shim database requires to match a file with database entry
243 *
244 * @note You must free the attr_info allocated by this function by calling SdbFreeFileAttributes.
245 *
246 * @param [in] path Path to the file.
247 * @param [out] attr_info_ret Pointer to array of ATTRINFO. Contains attribute data.
248 * @param [out] attr_count Number of attributes in attr_info.
249 *
250 * @return TRUE if it succeeds, FALSE if it fails.
251 */
252 BOOL WINAPI SdbGetFileAttributes(LPCWSTR path, PATTRINFO *attr_info_ret, LPDWORD attr_count)
253 {
254 static const WCHAR str_tinfo[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0};
255 static const WCHAR str_trans[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\','%','0','4','x','%','0','4','x','\\','%','%','s',0};
256 static const WCHAR str_CompanyName[] = {'C','o','m','p','a','n','y','N','a','m','e',0};
257 static const WCHAR str_FileDescription[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0};
258 static const WCHAR str_FileVersion[] = {'F','i','l','e','V','e','r','s','i','o','n',0};
259 static const WCHAR str_InternalName[] = {'I','n','t','e','r','n','a','l','N','a','m','e',0};
260 static const WCHAR str_LegalCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0};
261 static const WCHAR str_OriginalFilename[] = {'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0};
262 static const WCHAR str_ProductName[] = {'P','r','o','d','u','c','t','N','a','m','e',0};
263 static const WCHAR str_ProductVersion[] = {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
264
265 PIMAGE_NT_HEADERS headers;
266 MEMMAPPED mapped;
267 PBYTE mapping_end;
268 PVOID file_info = 0;
269 DWORD module_type;
270 WCHAR translation[128] = {0};
271 PATTRINFO attr_info;
272
273 struct LANGANDCODEPAGE {
274 WORD language;
275 WORD code_page;
276 } *lang_page;
277
278 if (!SdbpOpenMemMappedFile(path, &mapped))
279 {
280 SHIM_ERR("Error retrieving FILEINFO structure\n");
281 return FALSE;
282 }
283 mapping_end = mapped.view + mapped.size;
284
285 attr_info = (PATTRINFO)SdbAlloc(NUM_ATTRIBUTES * sizeof(ATTRINFO));
286
287 SdbpSetDWORDAttr(&attr_info[0], TAG_SIZE, mapped.size);
288 if (mapped.size)
289 SdbpSetDWORDAttr(&attr_info[1], TAG_CHECKSUM, SdbpCalculateFileChecksum(&mapped));
290 else
291 SdbpSetAttrFail(&attr_info[1]);
292 module_type = SdbpGetModuleType(&mapped, &headers);
293
294 if (module_type != MODTYPE_UNKNOWN)
295 SdbpSetDWORDAttr(&attr_info[16], TAG_MODULE_TYPE, module_type);
296 else
297 SdbpSetAttrFail(&attr_info[16]); /* TAG_MODULE_TYPE */
298
299 if (headers && module_type == MODTYPE_PE && ((PBYTE)(headers+1) <= mapping_end))
300 {
301 DWORD info_size;
302 SIZE_T export_dir_size;
303 PIMAGE_EXPORT_DIRECTORY export_dir;
304
305 info_size = GetFileVersionInfoSizeW(path, NULL);
306 if (info_size != 0)
307 {
308 UINT page_size = 0;
309 file_info = SdbAlloc(info_size);
310 GetFileVersionInfoW(path, 0, info_size, file_info);
311 VerQueryValueW(file_info, str_tinfo, (LPVOID)&lang_page, &page_size);
312 snprintfW(translation, 128, str_trans, lang_page->language, lang_page->code_page);
313 }
314
315 /* Handles 2, 3, 12, 13, 14, 15, 21, 22 */
316 SdbpReadFileVersion(attr_info, file_info);
317
318 SdbpSetStringAttr(&attr_info[4], TAG_PRODUCT_VERSION, SdbpGetStringAttr(translation, str_ProductVersion, file_info));
319 SdbpSetStringAttr(&attr_info[5], TAG_FILE_DESCRIPTION, SdbpGetStringAttr(translation, str_FileDescription, file_info));
320 SdbpSetStringAttr(&attr_info[6], TAG_COMPANY_NAME, SdbpGetStringAttr(translation, str_CompanyName, file_info));
321 SdbpSetStringAttr(&attr_info[7], TAG_PRODUCT_NAME, SdbpGetStringAttr(translation, str_ProductName, file_info));
322 SdbpSetStringAttr(&attr_info[8], TAG_FILE_VERSION, SdbpGetStringAttr(translation, str_FileVersion, file_info));
323 SdbpSetStringAttr(&attr_info[9], TAG_ORIGINAL_FILENAME, SdbpGetStringAttr(translation, str_OriginalFilename, file_info));
324 SdbpSetStringAttr(&attr_info[10], TAG_INTERNAL_NAME, SdbpGetStringAttr(translation, str_InternalName, file_info));
325 SdbpSetStringAttr(&attr_info[11], TAG_LEGAL_COPYRIGHT, SdbpGetStringAttr(translation, str_LegalCopyright, file_info));
326
327 /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx */
328
329 SdbpSetDWORDAttr(&attr_info[17], TAG_PE_CHECKSUM, headers->OptionalHeader.CheckSum);
330
331 SdbpSetDWORDAttr(&attr_info[18], TAG_LINKER_VERSION, /* mislabeled! */
332 ((DWORD)headers->OptionalHeader.MajorImageVersion) << 16 | headers->OptionalHeader.MinorImageVersion);
333 SdbpSetAttrFail(&attr_info[19]); /* TAG_16BIT_DESCRIPTION */
334 SdbpSetAttrFail(&attr_info[20]); /* TAG_16BIT_MODULE_NAME */
335
336 SdbpSetDWORDAttr(&attr_info[23], TAG_LINK_DATE, headers->FileHeader.TimeDateStamp);
337 SdbpSetDWORDAttr(&attr_info[24], TAG_UPTO_LINK_DATE, headers->FileHeader.TimeDateStamp);
338
339 export_dir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(mapped.view, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &export_dir_size);
340 if (export_dir && ((PBYTE)(export_dir+1) <= mapping_end))
341 {
342 PIMAGE_SECTION_HEADER section = NULL;
343 PBYTE export_name = RtlImageRvaToVa(headers, mapped.view, export_dir->Name, &section);
344 if (export_name)
345 SdbpSetStringAttrFromAnsiString(&attr_info[25], TAG_EXPORT_NAME, export_name, strlen((char*)export_name));
346 else
347 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
348 }
349 else
350 {
351 SdbpSetAttrFail(&attr_info[25]); /* TAG_EXPORT_NAME */
352 }
353
354 if (info_size)
355 SdbpSetDWORDAttr(&attr_info[26], TAG_VER_LANGUAGE, lang_page->language);
356
357 SdbpSetDWORDAttr(&attr_info[27], TAG_EXE_WRAPPER, 0); /* boolean */
358 }
359 else
360 {
361 int n;
362 for (n = 2; n < NUM_ATTRIBUTES; ++n)
363 {
364 if (n != 16 && n != 26)
365 SdbpSetAttrFail(&attr_info[n]);
366 }
367 if (module_type == MODTYPE_NE)
368 {
369 PBYTE ptr;
370 PIMAGE_OS2_HEADER os2 = (PIMAGE_OS2_HEADER)headers;
371 if ((PBYTE)(os2 + 1) <= mapping_end)
372 {
373 ptr = mapped.view + os2->ne_nrestab;
374 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
375 SdbpSetStringAttrFromPascalString(&attr_info[19], TAG_16BIT_DESCRIPTION, ptr);
376 ptr = (PBYTE)os2 + os2->ne_restab;
377 if (ptr <= mapping_end && (ptr + 1 + *ptr) <= mapping_end)
378 SdbpSetStringAttrFromPascalString(&attr_info[20], TAG_16BIT_MODULE_NAME, ptr);
379 }
380 }
381 }
382
383 *attr_info_ret = attr_info;
384 *attr_count = NUM_ATTRIBUTES; /* As far as I know, this one is always 28 */
385
386 SdbFree(file_info);
387 SdbpCloseMemMappedFile(&mapped);
388 return TRUE;
389 }