Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenappsearch.c
Go to the documentation of this file.
00001 /* 00002 * Implementation of the AppSearch action of the Microsoft Installer (msi.dll) 00003 * 00004 * Copyright 2005 Juan Lang 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00019 */ 00020 #include <stdarg.h> 00021 00022 #define COBJMACROS 00023 00024 #include "windef.h" 00025 #include "winbase.h" 00026 #include "winreg.h" 00027 #include "msi.h" 00028 #include "msiquery.h" 00029 #include "msidefs.h" 00030 #include "winver.h" 00031 #include "shlwapi.h" 00032 #include "wine/unicode.h" 00033 #include "wine/debug.h" 00034 #include "msipriv.h" 00035 00036 WINE_DEFAULT_DEBUG_CHANNEL(msi); 00037 00038 typedef struct tagMSISIGNATURE 00039 { 00040 LPCWSTR Name; /* NOT owned by this structure */ 00041 LPWSTR File; 00042 DWORD MinVersionMS; 00043 DWORD MinVersionLS; 00044 DWORD MaxVersionMS; 00045 DWORD MaxVersionLS; 00046 DWORD MinSize; 00047 DWORD MaxSize; 00048 FILETIME MinTime; 00049 FILETIME MaxTime; 00050 LPWSTR Languages; 00051 }MSISIGNATURE; 00052 00053 void msi_parse_version_string(LPCWSTR verStr, PDWORD ms, PDWORD ls) 00054 { 00055 const WCHAR *ptr; 00056 int x1 = 0, x2 = 0, x3 = 0, x4 = 0; 00057 00058 x1 = atoiW(verStr); 00059 ptr = strchrW(verStr, '.'); 00060 if (ptr) 00061 { 00062 x2 = atoiW(ptr + 1); 00063 ptr = strchrW(ptr + 1, '.'); 00064 } 00065 if (ptr) 00066 { 00067 x3 = atoiW(ptr + 1); 00068 ptr = strchrW(ptr + 1, '.'); 00069 } 00070 if (ptr) 00071 x4 = atoiW(ptr + 1); 00072 /* FIXME: byte-order dependent? */ 00073 *ms = x1 << 16 | x2; 00074 if (ls) *ls = x3 << 16 | x4; 00075 } 00076 00077 /* Fills in sig with the values from the Signature table, where name is the 00078 * signature to find. Upon return, sig->File will be NULL if the record is not 00079 * found, and not NULL if it is found. 00080 * Warning: clears all fields in sig! 00081 * Returns ERROR_SUCCESS upon success (where not finding the record counts as 00082 * success), something else on error. 00083 */ 00084 static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, LPCWSTR name) 00085 { 00086 static const WCHAR query[] = { 00087 's','e','l','e','c','t',' ','*',' ', 00088 'f','r','o','m',' ', 00089 'S','i','g','n','a','t','u','r','e',' ', 00090 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ', 00091 '\'','%','s','\'',0}; 00092 LPWSTR minVersion, maxVersion, p; 00093 MSIRECORD *row; 00094 DWORD time; 00095 00096 TRACE("package %p, sig %p\n", package, sig); 00097 00098 memset(sig, 0, sizeof(*sig)); 00099 sig->Name = name; 00100 row = MSI_QueryGetRecord( package->db, query, name ); 00101 if (!row) 00102 { 00103 TRACE("failed to query signature for %s\n", debugstr_w(name)); 00104 return ERROR_SUCCESS; 00105 } 00106 00107 /* get properties */ 00108 sig->File = msi_dup_record_field(row,2); 00109 if ((p = strchrW(sig->File, '|'))) 00110 { 00111 p++; 00112 memmove(sig->File, p, (strlenW(p) + 1) * sizeof(WCHAR)); 00113 } 00114 00115 minVersion = msi_dup_record_field(row,3); 00116 if (minVersion) 00117 { 00118 msi_parse_version_string( minVersion, &sig->MinVersionMS, &sig->MinVersionLS ); 00119 msi_free( minVersion ); 00120 } 00121 maxVersion = msi_dup_record_field(row,4); 00122 if (maxVersion) 00123 { 00124 msi_parse_version_string( maxVersion, &sig->MaxVersionMS, &sig->MaxVersionLS ); 00125 msi_free( maxVersion ); 00126 } 00127 sig->MinSize = MSI_RecordGetInteger(row,5); 00128 if (sig->MinSize == MSI_NULL_INTEGER) 00129 sig->MinSize = 0; 00130 sig->MaxSize = MSI_RecordGetInteger(row,6); 00131 if (sig->MaxSize == MSI_NULL_INTEGER) 00132 sig->MaxSize = 0; 00133 sig->Languages = msi_dup_record_field(row,9); 00134 time = MSI_RecordGetInteger(row,7); 00135 if (time != MSI_NULL_INTEGER) 00136 DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime); 00137 time = MSI_RecordGetInteger(row,8); 00138 if (time != MSI_NULL_INTEGER) 00139 DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime); 00140 00141 TRACE("Found file name %s for Signature_ %s;\n", 00142 debugstr_w(sig->File), debugstr_w(name)); 00143 TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS), 00144 LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS), 00145 LOWORD(sig->MinVersionLS)); 00146 TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS), 00147 LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS), 00148 LOWORD(sig->MaxVersionLS)); 00149 TRACE("MinSize is %d, MaxSize is %d;\n", sig->MinSize, sig->MaxSize); 00150 TRACE("Languages is %s\n", debugstr_w(sig->Languages)); 00151 00152 msiobj_release( &row->hdr ); 00153 00154 return ERROR_SUCCESS; 00155 } 00156 00157 /* Frees any memory allocated in sig */ 00158 static void ACTION_FreeSignature(MSISIGNATURE *sig) 00159 { 00160 msi_free(sig->File); 00161 msi_free(sig->Languages); 00162 } 00163 00164 static LPWSTR app_search_file(LPWSTR path, MSISIGNATURE *sig) 00165 { 00166 VS_FIXEDFILEINFO *info; 00167 DWORD attr, handle, size; 00168 LPWSTR val = NULL; 00169 LPBYTE buffer; 00170 00171 if (!sig->File) 00172 { 00173 PathRemoveFileSpecW(path); 00174 PathAddBackslashW(path); 00175 00176 attr = GetFileAttributesW(path); 00177 if (attr != INVALID_FILE_ATTRIBUTES && 00178 (attr & FILE_ATTRIBUTE_DIRECTORY)) 00179 return strdupW(path); 00180 00181 return NULL; 00182 } 00183 00184 attr = GetFileAttributesW(path); 00185 if (attr == INVALID_FILE_ATTRIBUTES || 00186 (attr & FILE_ATTRIBUTE_DIRECTORY)) 00187 return NULL; 00188 00189 size = GetFileVersionInfoSizeW(path, &handle); 00190 if (!size) 00191 return strdupW(path); 00192 00193 buffer = msi_alloc(size); 00194 if (!buffer) 00195 return NULL; 00196 00197 if (!GetFileVersionInfoW(path, 0, size, buffer)) 00198 goto done; 00199 00200 if (!VerQueryValueW(buffer, szBackSlash, (LPVOID)&info, &size) || !info) 00201 goto done; 00202 00203 if (sig->MinVersionLS || sig->MinVersionMS) 00204 { 00205 if (info->dwFileVersionMS < sig->MinVersionMS) 00206 goto done; 00207 00208 if (info->dwFileVersionMS == sig->MinVersionMS && 00209 info->dwFileVersionLS < sig->MinVersionLS) 00210 goto done; 00211 } 00212 00213 if (sig->MaxVersionLS || sig->MaxVersionMS) 00214 { 00215 if (info->dwFileVersionMS > sig->MaxVersionMS) 00216 goto done; 00217 00218 if (info->dwFileVersionMS == sig->MaxVersionMS && 00219 info->dwFileVersionLS > sig->MaxVersionLS) 00220 goto done; 00221 } 00222 00223 val = strdupW(path); 00224 00225 done: 00226 msi_free(buffer); 00227 return val; 00228 } 00229 00230 static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) 00231 { 00232 static const WCHAR query[] = { 00233 'S','E','L','E','C','T',' ','*',' ', 00234 'F','R','O','M',' ', 00235 '`','C','o','m','p','L','o','c','a','t','o','r','`',' ', 00236 'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','_','`',' ','=',' ', 00237 '\'','%','s','\'',0}; 00238 static const WCHAR sigquery[] = { 00239 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 00240 '`','S','i','g','n','a','t','u','r','e','`',' ', 00241 'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','`',' ','=',' ', 00242 '\'','%','s','\'',0}; 00243 00244 MSIRECORD *row, *rec; 00245 LPCWSTR signature, guid; 00246 BOOL sigpresent = TRUE; 00247 BOOL isdir; 00248 UINT type; 00249 WCHAR path[MAX_PATH]; 00250 DWORD size = MAX_PATH; 00251 LPWSTR ptr; 00252 DWORD attr; 00253 00254 TRACE("%s\n", debugstr_w(sig->Name)); 00255 00256 *appValue = NULL; 00257 00258 row = MSI_QueryGetRecord(package->db, query, sig->Name); 00259 if (!row) 00260 { 00261 TRACE("failed to query CompLocator for %s\n", debugstr_w(sig->Name)); 00262 return ERROR_SUCCESS; 00263 } 00264 00265 signature = MSI_RecordGetString(row, 1); 00266 guid = MSI_RecordGetString(row, 2); 00267 type = MSI_RecordGetInteger(row, 3); 00268 00269 rec = MSI_QueryGetRecord(package->db, sigquery, signature); 00270 if (!rec) 00271 sigpresent = FALSE; 00272 00273 *path = '\0'; 00274 MsiLocateComponentW(guid, path, &size); 00275 if (!*path) 00276 goto done; 00277 00278 attr = GetFileAttributesW(path); 00279 if (attr == INVALID_FILE_ATTRIBUTES) 00280 goto done; 00281 00282 isdir = (attr & FILE_ATTRIBUTE_DIRECTORY); 00283 00284 if (type != msidbLocatorTypeDirectory && sigpresent && !isdir) 00285 { 00286 *appValue = app_search_file(path, sig); 00287 } 00288 else if (!sigpresent && (type != msidbLocatorTypeDirectory || isdir)) 00289 { 00290 if (type == msidbLocatorTypeFileName) 00291 { 00292 ptr = strrchrW(path, '\\'); 00293 *(ptr + 1) = '\0'; 00294 } 00295 else 00296 PathAddBackslashW(path); 00297 00298 *appValue = strdupW(path); 00299 } 00300 else if (sigpresent) 00301 { 00302 PathAddBackslashW(path); 00303 lstrcatW(path, MSI_RecordGetString(rec, 2)); 00304 00305 attr = GetFileAttributesW(path); 00306 if (attr != INVALID_FILE_ATTRIBUTES && 00307 !(attr & FILE_ATTRIBUTE_DIRECTORY)) 00308 *appValue = strdupW(path); 00309 } 00310 00311 done: 00312 if (rec) msiobj_release(&rec->hdr); 00313 msiobj_release(&row->hdr); 00314 return ERROR_SUCCESS; 00315 } 00316 00317 static void ACTION_ConvertRegValue(DWORD regType, const BYTE *value, DWORD sz, 00318 LPWSTR *appValue) 00319 { 00320 static const WCHAR dwordFmt[] = { '#','%','d','\0' }; 00321 static const WCHAR binPre[] = { '#','x','\0' }; 00322 static const WCHAR binFmt[] = { '%','0','2','X','\0' }; 00323 LPWSTR ptr; 00324 DWORD i; 00325 00326 switch (regType) 00327 { 00328 case REG_SZ: 00329 if (*(LPCWSTR)value == '#') 00330 { 00331 /* escape leading pound with another */ 00332 *appValue = msi_alloc(sz + sizeof(WCHAR)); 00333 (*appValue)[0] = '#'; 00334 strcpyW(*appValue + 1, (LPCWSTR)value); 00335 } 00336 else 00337 { 00338 *appValue = msi_alloc(sz); 00339 strcpyW(*appValue, (LPCWSTR)value); 00340 } 00341 break; 00342 case REG_DWORD: 00343 /* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign 00344 * char if needed 00345 */ 00346 *appValue = msi_alloc(10 * sizeof(WCHAR)); 00347 sprintfW(*appValue, dwordFmt, *(const DWORD *)value); 00348 break; 00349 case REG_EXPAND_SZ: 00350 sz = ExpandEnvironmentStringsW((LPCWSTR)value, NULL, 0); 00351 *appValue = msi_alloc(sz * sizeof(WCHAR)); 00352 ExpandEnvironmentStringsW((LPCWSTR)value, *appValue, sz); 00353 break; 00354 case REG_BINARY: 00355 /* #x<nibbles>\0 */ 00356 *appValue = msi_alloc((sz * 2 + 3) * sizeof(WCHAR)); 00357 lstrcpyW(*appValue, binPre); 00358 ptr = *appValue + lstrlenW(binPre); 00359 for (i = 0; i < sz; i++, ptr += 2) 00360 sprintfW(ptr, binFmt, value[i]); 00361 break; 00362 default: 00363 WARN("unimplemented for values of type %d\n", regType); 00364 *appValue = NULL; 00365 } 00366 } 00367 00368 static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, 00369 LPCWSTR path, int depth, LPWSTR *appValue); 00370 00371 static UINT ACTION_AppSearchReg(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) 00372 { 00373 static const WCHAR query[] = { 00374 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 00375 'R','e','g','L','o','c','a','t','o','r',' ','W','H','E','R','E',' ', 00376 'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0}; 00377 const WCHAR *keyPath, *valueName; 00378 WCHAR *deformatted = NULL, *ptr = NULL, *end; 00379 int root, type; 00380 HKEY rootKey, key = NULL; 00381 DWORD sz = 0, regType; 00382 LPBYTE value = NULL; 00383 MSIRECORD *row; 00384 UINT rc; 00385 00386 TRACE("%s\n", debugstr_w(sig->Name)); 00387 00388 *appValue = NULL; 00389 00390 row = MSI_QueryGetRecord( package->db, query, sig->Name ); 00391 if (!row) 00392 { 00393 TRACE("failed to query RegLocator for %s\n", debugstr_w(sig->Name)); 00394 return ERROR_SUCCESS; 00395 } 00396 00397 root = MSI_RecordGetInteger(row, 2); 00398 keyPath = MSI_RecordGetString(row, 3); 00399 valueName = MSI_RecordGetString(row, 4); 00400 type = MSI_RecordGetInteger(row, 5); 00401 00402 deformat_string(package, keyPath, &deformatted); 00403 00404 switch (root) 00405 { 00406 case msidbRegistryRootClassesRoot: 00407 rootKey = HKEY_CLASSES_ROOT; 00408 break; 00409 case msidbRegistryRootCurrentUser: 00410 rootKey = HKEY_CURRENT_USER; 00411 break; 00412 case msidbRegistryRootLocalMachine: 00413 rootKey = HKEY_LOCAL_MACHINE; 00414 break; 00415 case msidbRegistryRootUsers: 00416 rootKey = HKEY_USERS; 00417 break; 00418 default: 00419 WARN("Unknown root key %d\n", root); 00420 goto end; 00421 } 00422 00423 rc = RegOpenKeyW(rootKey, deformatted, &key); 00424 if (rc) 00425 { 00426 TRACE("RegOpenKeyW returned %d\n", rc); 00427 goto end; 00428 } 00429 00430 msi_free(deformatted); 00431 deformat_string(package, valueName, &deformatted); 00432 00433 rc = RegQueryValueExW(key, deformatted, NULL, NULL, NULL, &sz); 00434 if (rc) 00435 { 00436 TRACE("RegQueryValueExW returned %d\n", rc); 00437 goto end; 00438 } 00439 /* FIXME: sanity-check sz before allocating (is there an upper-limit 00440 * on the value of a property?) 00441 */ 00442 value = msi_alloc( sz ); 00443 rc = RegQueryValueExW(key, deformatted, NULL, ®Type, value, &sz); 00444 if (rc) 00445 { 00446 TRACE("RegQueryValueExW returned %d\n", rc); 00447 goto end; 00448 } 00449 00450 /* bail out if the registry key is empty */ 00451 if (sz == 0) 00452 goto end; 00453 00454 if ((regType == REG_SZ || regType == REG_EXPAND_SZ) && 00455 (ptr = strchrW((LPWSTR)value, '"')) && (end = strchrW(++ptr, '"'))) 00456 *end = '\0'; 00457 else 00458 ptr = (LPWSTR)value; 00459 00460 switch (type & 0x0f) 00461 { 00462 case msidbLocatorTypeDirectory: 00463 ACTION_SearchDirectory(package, sig, ptr, 0, appValue); 00464 break; 00465 case msidbLocatorTypeFileName: 00466 *appValue = app_search_file(ptr, sig); 00467 break; 00468 case msidbLocatorTypeRawValue: 00469 ACTION_ConvertRegValue(regType, value, sz, appValue); 00470 break; 00471 default: 00472 FIXME("unimplemented for type %d (key path %s, value %s)\n", 00473 type, debugstr_w(keyPath), debugstr_w(valueName)); 00474 } 00475 end: 00476 msi_free( value ); 00477 RegCloseKey( key ); 00478 msi_free( deformatted ); 00479 00480 msiobj_release(&row->hdr); 00481 return ERROR_SUCCESS; 00482 } 00483 00484 static LPWSTR get_ini_field(LPWSTR buf, int field) 00485 { 00486 LPWSTR beg, end; 00487 int i = 1; 00488 00489 if (field == 0) 00490 return strdupW(buf); 00491 00492 beg = buf; 00493 while ((end = strchrW(beg, ',')) && i < field) 00494 { 00495 beg = end + 1; 00496 while (*beg && *beg == ' ') 00497 beg++; 00498 00499 i++; 00500 } 00501 00502 end = strchrW(beg, ','); 00503 if (!end) 00504 end = beg + lstrlenW(beg); 00505 00506 *end = '\0'; 00507 return strdupW(beg); 00508 } 00509 00510 static UINT ACTION_AppSearchIni(MSIPACKAGE *package, LPWSTR *appValue, 00511 MSISIGNATURE *sig) 00512 { 00513 static const WCHAR query[] = { 00514 's','e','l','e','c','t',' ','*',' ', 00515 'f','r','o','m',' ', 00516 'I','n','i','L','o','c','a','t','o','r',' ', 00517 'w','h','e','r','e',' ', 00518 'S','i','g','n','a','t','u','r','e','_',' ','=',' ','\'','%','s','\'',0}; 00519 MSIRECORD *row; 00520 LPWSTR fileName, section, key; 00521 int field, type; 00522 WCHAR buf[MAX_PATH]; 00523 00524 TRACE("%s\n", debugstr_w(sig->Name)); 00525 00526 *appValue = NULL; 00527 00528 row = MSI_QueryGetRecord( package->db, query, sig->Name ); 00529 if (!row) 00530 { 00531 TRACE("failed to query IniLocator for %s\n", debugstr_w(sig->Name)); 00532 return ERROR_SUCCESS; 00533 } 00534 00535 fileName = msi_dup_record_field(row, 2); 00536 section = msi_dup_record_field(row, 3); 00537 key = msi_dup_record_field(row, 4); 00538 field = MSI_RecordGetInteger(row, 5); 00539 type = MSI_RecordGetInteger(row, 6); 00540 if (field == MSI_NULL_INTEGER) 00541 field = 0; 00542 if (type == MSI_NULL_INTEGER) 00543 type = 0; 00544 00545 GetPrivateProfileStringW(section, key, NULL, buf, MAX_PATH, fileName); 00546 if (buf[0]) 00547 { 00548 switch (type & 0x0f) 00549 { 00550 case msidbLocatorTypeDirectory: 00551 ACTION_SearchDirectory(package, sig, buf, 0, appValue); 00552 break; 00553 case msidbLocatorTypeFileName: 00554 *appValue = app_search_file(buf, sig); 00555 break; 00556 case msidbLocatorTypeRawValue: 00557 *appValue = get_ini_field(buf, field); 00558 break; 00559 } 00560 } 00561 00562 msi_free(fileName); 00563 msi_free(section); 00564 msi_free(key); 00565 00566 msiobj_release(&row->hdr); 00567 00568 return ERROR_SUCCESS; 00569 } 00570 00571 /* Expands the value in src into a path without property names and only 00572 * containing long path names into dst. Replaces at most len characters of dst, 00573 * and always NULL-terminates dst if dst is not NULL and len >= 1. 00574 * May modify src. 00575 * Assumes src and dst are non-overlapping. 00576 * FIXME: return code probably needed: 00577 * - what does AppSearch return if the table values are invalid? 00578 * - what if dst is too small? 00579 */ 00580 static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst, 00581 size_t len) 00582 { 00583 WCHAR *ptr, *deformatted; 00584 00585 if (!src || !dst || !len) 00586 { 00587 if (dst) *dst = '\0'; 00588 return; 00589 } 00590 00591 dst[0] = '\0'; 00592 00593 /* Ignore the short portion of the path */ 00594 if ((ptr = strchrW(src, '|'))) 00595 ptr++; 00596 else 00597 ptr = src; 00598 00599 deformat_string(package, ptr, &deformatted); 00600 if (!deformatted || strlenW(deformatted) > len - 1) 00601 { 00602 msi_free(deformatted); 00603 return; 00604 } 00605 00606 lstrcpyW(dst, deformatted); 00607 dst[lstrlenW(deformatted)] = '\0'; 00608 msi_free(deformatted); 00609 } 00610 00611 static LANGID *parse_languages( const WCHAR *languages, DWORD *num_ids ) 00612 { 00613 UINT i, count = 1; 00614 WCHAR *str = strdupW( languages ), *p, *q; 00615 LANGID *ret; 00616 00617 if (!str) return NULL; 00618 for (p = q = str; (q = strchrW( q, ',' )); q++) count++; 00619 00620 if (!(ret = msi_alloc( count * sizeof(LANGID) ))) 00621 { 00622 msi_free( str ); 00623 return NULL; 00624 } 00625 i = 0; 00626 while (*p) 00627 { 00628 q = strchrW( p, ',' ); 00629 if (q) *q = 0; 00630 ret[i] = atoiW( p ); 00631 if (!q) break; 00632 p = q + 1; 00633 i++; 00634 } 00635 msi_free( str ); 00636 *num_ids = count; 00637 return ret; 00638 } 00639 00640 static BOOL match_languages( const void *version, const WCHAR *languages ) 00641 { 00642 struct lang 00643 { 00644 USHORT id; 00645 USHORT codepage; 00646 } *lang; 00647 DWORD len, num_ids, i, j; 00648 BOOL found = FALSE; 00649 LANGID *ids; 00650 00651 if (!languages || !languages[0]) return TRUE; 00652 if (!VerQueryValueW( version, szLangResource, (void **)&lang, &len )) return FALSE; 00653 if (!(ids = parse_languages( languages, &num_ids ))) return FALSE; 00654 00655 for (i = 0; i < num_ids; i++) 00656 { 00657 found = FALSE; 00658 for (j = 0; j < len / sizeof(struct lang); j++) 00659 { 00660 if (!ids[i] || ids[i] == lang[j].id) found = TRUE; 00661 } 00662 if (!found) goto done; 00663 } 00664 00665 done: 00666 msi_free( ids ); 00667 return found; 00668 } 00669 00670 /* Sets *matches to whether the file (whose path is filePath) matches the 00671 * versions set in sig. 00672 * Return ERROR_SUCCESS in case of success (whether or not the file matches), 00673 * something else if an install-halting error occurs. 00674 */ 00675 static UINT ACTION_FileVersionMatches(const MSISIGNATURE *sig, LPCWSTR filePath, 00676 BOOL *matches) 00677 { 00678 UINT len; 00679 void *version; 00680 VS_FIXEDFILEINFO *info = NULL; 00681 DWORD zero, size = GetFileVersionInfoSizeW( filePath, &zero ); 00682 00683 *matches = FALSE; 00684 00685 if (!size) return ERROR_SUCCESS; 00686 if (!(version = msi_alloc( size ))) return ERROR_OUTOFMEMORY; 00687 00688 if (GetFileVersionInfoW( filePath, 0, size, version )) 00689 VerQueryValueW( version, szBackSlash, (void **)&info, &len ); 00690 00691 if (info) 00692 { 00693 TRACE("comparing file version %d.%d.%d.%d:\n", 00694 HIWORD(info->dwFileVersionMS), 00695 LOWORD(info->dwFileVersionMS), 00696 HIWORD(info->dwFileVersionLS), 00697 LOWORD(info->dwFileVersionLS)); 00698 if (info->dwFileVersionMS < sig->MinVersionMS 00699 || (info->dwFileVersionMS == sig->MinVersionMS && 00700 info->dwFileVersionLS < sig->MinVersionLS)) 00701 { 00702 TRACE("less than minimum version %d.%d.%d.%d\n", 00703 HIWORD(sig->MinVersionMS), 00704 LOWORD(sig->MinVersionMS), 00705 HIWORD(sig->MinVersionLS), 00706 LOWORD(sig->MinVersionLS)); 00707 } 00708 else if ((sig->MaxVersionMS || sig->MaxVersionLS) && 00709 (info->dwFileVersionMS > sig->MaxVersionMS || 00710 (info->dwFileVersionMS == sig->MaxVersionMS && 00711 info->dwFileVersionLS > sig->MaxVersionLS))) 00712 { 00713 TRACE("greater than maximum version %d.%d.%d.%d\n", 00714 HIWORD(sig->MaxVersionMS), 00715 LOWORD(sig->MaxVersionMS), 00716 HIWORD(sig->MaxVersionLS), 00717 LOWORD(sig->MaxVersionLS)); 00718 } 00719 else if (!match_languages( version, sig->Languages )) 00720 { 00721 TRACE("languages %s not supported\n", debugstr_w( sig->Languages )); 00722 } 00723 else *matches = TRUE; 00724 } 00725 msi_free( version ); 00726 return ERROR_SUCCESS; 00727 } 00728 00729 /* Sets *matches to whether the file in findData matches that in sig. 00730 * fullFilePath is assumed to be the full path of the file specified in 00731 * findData, which may be necessary to compare the version. 00732 * Return ERROR_SUCCESS in case of success (whether or not the file matches), 00733 * something else if an install-halting error occurs. 00734 */ 00735 static UINT ACTION_FileMatchesSig(const MSISIGNATURE *sig, 00736 const WIN32_FIND_DATAW *findData, LPCWSTR fullFilePath, BOOL *matches) 00737 { 00738 UINT rc = ERROR_SUCCESS; 00739 00740 *matches = TRUE; 00741 /* assumes the caller has already ensured the filenames match, so check 00742 * the other fields.. 00743 */ 00744 if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime) 00745 { 00746 if (findData->ftCreationTime.dwHighDateTime < 00747 sig->MinTime.dwHighDateTime || 00748 (findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime 00749 && findData->ftCreationTime.dwLowDateTime < 00750 sig->MinTime.dwLowDateTime)) 00751 *matches = FALSE; 00752 } 00753 if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime)) 00754 { 00755 if (findData->ftCreationTime.dwHighDateTime > 00756 sig->MaxTime.dwHighDateTime || 00757 (findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime 00758 && findData->ftCreationTime.dwLowDateTime > 00759 sig->MaxTime.dwLowDateTime)) 00760 *matches = FALSE; 00761 } 00762 if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize) 00763 *matches = FALSE; 00764 if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize) 00765 *matches = FALSE; 00766 if (*matches && (sig->MinVersionMS || sig->MinVersionLS || 00767 sig->MaxVersionMS || sig->MaxVersionLS)) 00768 rc = ACTION_FileVersionMatches(sig, fullFilePath, matches); 00769 return rc; 00770 } 00771 00772 /* Recursively searches the directory dir for files that match the signature 00773 * sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir 00774 * (and only dir). If depth is 1, searches dir and its immediate 00775 * subdirectories. 00776 * Assumes sig->File is not NULL. 00777 * Returns ERROR_SUCCESS on success (which may include non-critical errors), 00778 * something else on failures which should halt the install. 00779 */ 00780 static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, LPWSTR *appValue, 00781 MSISIGNATURE *sig, LPCWSTR dir, int depth) 00782 { 00783 HANDLE hFind; 00784 WIN32_FIND_DATAW findData; 00785 UINT rc = ERROR_SUCCESS; 00786 size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File); 00787 WCHAR subpath[MAX_PATH]; 00788 WCHAR *buf; 00789 DWORD len; 00790 00791 static const WCHAR starDotStarW[] = { '*','.','*',0 }; 00792 00793 TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir), 00794 debugstr_w(sig->File), depth); 00795 00796 if (depth < 0) 00797 return ERROR_SUCCESS; 00798 00799 *appValue = NULL; 00800 /* We need the buffer in both paths below, so go ahead and allocate it 00801 * here. Add two because we might need to add a backslash if the dir name 00802 * isn't backslash-terminated. 00803 */ 00804 len = dirLen + max(fileLen, strlenW(starDotStarW)) + 2; 00805 buf = msi_alloc(len * sizeof(WCHAR)); 00806 if (!buf) 00807 return ERROR_OUTOFMEMORY; 00808 00809 lstrcpyW(buf, dir); 00810 PathAddBackslashW(buf); 00811 lstrcatW(buf, sig->File); 00812 00813 hFind = FindFirstFileW(buf, &findData); 00814 if (hFind != INVALID_HANDLE_VALUE) 00815 { 00816 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 00817 { 00818 BOOL matches; 00819 00820 rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches); 00821 if (rc == ERROR_SUCCESS && matches) 00822 { 00823 TRACE("found file, returning %s\n", debugstr_w(buf)); 00824 *appValue = buf; 00825 } 00826 } 00827 FindClose(hFind); 00828 } 00829 00830 if (rc == ERROR_SUCCESS && !*appValue) 00831 { 00832 lstrcpyW(buf, dir); 00833 PathAddBackslashW(buf); 00834 lstrcatW(buf, starDotStarW); 00835 00836 hFind = FindFirstFileW(buf, &findData); 00837 if (hFind != INVALID_HANDLE_VALUE) 00838 { 00839 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && 00840 strcmpW( findData.cFileName, szDot ) && 00841 strcmpW( findData.cFileName, szDotDot )) 00842 { 00843 lstrcpyW(subpath, dir); 00844 PathAppendW(subpath, findData.cFileName); 00845 rc = ACTION_RecurseSearchDirectory(package, appValue, sig, 00846 subpath, depth - 1); 00847 } 00848 00849 while (rc == ERROR_SUCCESS && !*appValue && 00850 FindNextFileW(hFind, &findData) != 0) 00851 { 00852 if (!strcmpW( findData.cFileName, szDot ) || 00853 !strcmpW( findData.cFileName, szDotDot )) 00854 continue; 00855 00856 lstrcpyW(subpath, dir); 00857 PathAppendW(subpath, findData.cFileName); 00858 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 00859 rc = ACTION_RecurseSearchDirectory(package, appValue, 00860 sig, subpath, depth - 1); 00861 } 00862 00863 FindClose(hFind); 00864 } 00865 } 00866 00867 if (*appValue != buf) 00868 msi_free(buf); 00869 00870 return rc; 00871 } 00872 00873 static UINT ACTION_CheckDirectory(MSIPACKAGE *package, LPCWSTR dir, 00874 LPWSTR *appValue) 00875 { 00876 DWORD attr = GetFileAttributesW(dir); 00877 00878 if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) 00879 { 00880 TRACE("directory exists, returning %s\n", debugstr_w(dir)); 00881 *appValue = strdupW(dir); 00882 } 00883 00884 return ERROR_SUCCESS; 00885 } 00886 00887 static BOOL ACTION_IsFullPath(LPCWSTR path) 00888 { 00889 WCHAR first = toupperW(path[0]); 00890 BOOL ret; 00891 00892 if (first >= 'A' && first <= 'Z' && path[1] == ':') 00893 ret = TRUE; 00894 else if (path[0] == '\\' && path[1] == '\\') 00895 ret = TRUE; 00896 else 00897 ret = FALSE; 00898 return ret; 00899 } 00900 00901 static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, 00902 LPCWSTR path, int depth, LPWSTR *appValue) 00903 { 00904 UINT rc; 00905 DWORD attr; 00906 LPWSTR val = NULL; 00907 00908 TRACE("%p, %p, %s, %d, %p\n", package, sig, debugstr_w(path), depth, 00909 appValue); 00910 00911 if (ACTION_IsFullPath(path)) 00912 { 00913 if (sig->File) 00914 rc = ACTION_RecurseSearchDirectory(package, &val, sig, path, depth); 00915 else 00916 { 00917 /* Recursively searching a directory makes no sense when the 00918 * directory to search is the thing you're trying to find. 00919 */ 00920 rc = ACTION_CheckDirectory(package, path, &val); 00921 } 00922 } 00923 else 00924 { 00925 WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 }; 00926 DWORD drives = GetLogicalDrives(); 00927 int i; 00928 00929 rc = ERROR_SUCCESS; 00930 for (i = 0; rc == ERROR_SUCCESS && !val && i < 26; i++) 00931 { 00932 if (!(drives & (1 << i))) 00933 continue; 00934 00935 pathWithDrive[0] = 'A' + i; 00936 if (GetDriveTypeW(pathWithDrive) != DRIVE_FIXED) 00937 continue; 00938 00939 lstrcpynW(pathWithDrive + 3, path, 00940 sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3); 00941 00942 if (sig->File) 00943 rc = ACTION_RecurseSearchDirectory(package, &val, sig, 00944 pathWithDrive, depth); 00945 else 00946 rc = ACTION_CheckDirectory(package, pathWithDrive, &val); 00947 } 00948 } 00949 00950 attr = GetFileAttributesW(val); 00951 if (attr != INVALID_FILE_ATTRIBUTES && 00952 (attr & FILE_ATTRIBUTE_DIRECTORY) && 00953 val && val[lstrlenW(val) - 1] != '\\') 00954 { 00955 val = msi_realloc(val, (lstrlenW(val) + 2) * sizeof(WCHAR)); 00956 if (!val) 00957 rc = ERROR_OUTOFMEMORY; 00958 else 00959 PathAddBackslashW(val); 00960 } 00961 00962 *appValue = val; 00963 00964 TRACE("returning %d\n", rc); 00965 return rc; 00966 } 00967 00968 static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName, 00969 MSISIGNATURE *sig, LPWSTR *appValue); 00970 00971 static UINT ACTION_AppSearchDr(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig) 00972 { 00973 static const WCHAR query[] = { 00974 's','e','l','e','c','t',' ','*',' ', 00975 'f','r','o','m',' ', 00976 'D','r','L','o','c','a','t','o','r',' ', 00977 'w','h','e','r','e',' ', 00978 'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0}; 00979 LPWSTR parent = NULL; 00980 LPCWSTR parentName; 00981 WCHAR path[MAX_PATH]; 00982 WCHAR expanded[MAX_PATH]; 00983 MSIRECORD *row; 00984 int depth; 00985 DWORD sz, attr; 00986 UINT rc; 00987 00988 TRACE("%s\n", debugstr_w(sig->Name)); 00989 00990 *appValue = NULL; 00991 00992 row = MSI_QueryGetRecord( package->db, query, sig->Name ); 00993 if (!row) 00994 { 00995 TRACE("failed to query DrLocator for %s\n", debugstr_w(sig->Name)); 00996 return ERROR_SUCCESS; 00997 } 00998 00999 /* check whether parent is set */ 01000 parentName = MSI_RecordGetString(row, 2); 01001 if (parentName) 01002 { 01003 MSISIGNATURE parentSig; 01004 01005 ACTION_AppSearchSigName(package, parentName, &parentSig, &parent); 01006 ACTION_FreeSignature(&parentSig); 01007 if (!parent) 01008 { 01009 msiobj_release(&row->hdr); 01010 return ERROR_SUCCESS; 01011 } 01012 } 01013 01014 sz = MAX_PATH; 01015 MSI_RecordGetStringW(row, 3, path, &sz); 01016 01017 if (MSI_RecordIsNull(row,4)) 01018 depth = 0; 01019 else 01020 depth = MSI_RecordGetInteger(row,4); 01021 01022 if (sz) 01023 ACTION_ExpandAnyPath(package, path, expanded, MAX_PATH); 01024 else 01025 strcpyW(expanded, path); 01026 01027 if (parent) 01028 { 01029 attr = GetFileAttributesW(parent); 01030 if (attr != INVALID_FILE_ATTRIBUTES && 01031 !(attr & FILE_ATTRIBUTE_DIRECTORY)) 01032 { 01033 PathRemoveFileSpecW(parent); 01034 PathAddBackslashW(parent); 01035 } 01036 01037 strcpyW(path, parent); 01038 strcatW(path, expanded); 01039 } 01040 else if (sz) 01041 strcpyW(path, expanded); 01042 01043 PathAddBackslashW(path); 01044 01045 rc = ACTION_SearchDirectory(package, sig, path, depth, appValue); 01046 01047 msi_free(parent); 01048 msiobj_release(&row->hdr); 01049 01050 TRACE("returning %d\n", rc); 01051 return rc; 01052 } 01053 01054 static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName, 01055 MSISIGNATURE *sig, LPWSTR *appValue) 01056 { 01057 UINT rc; 01058 01059 *appValue = NULL; 01060 rc = ACTION_AppSearchGetSignature(package, sig, sigName); 01061 if (rc == ERROR_SUCCESS) 01062 { 01063 rc = ACTION_AppSearchComponents(package, appValue, sig); 01064 if (rc == ERROR_SUCCESS && !*appValue) 01065 { 01066 rc = ACTION_AppSearchReg(package, appValue, sig); 01067 if (rc == ERROR_SUCCESS && !*appValue) 01068 { 01069 rc = ACTION_AppSearchIni(package, appValue, sig); 01070 if (rc == ERROR_SUCCESS && !*appValue) 01071 rc = ACTION_AppSearchDr(package, appValue, sig); 01072 } 01073 } 01074 } 01075 return rc; 01076 } 01077 01078 static UINT iterate_appsearch(MSIRECORD *row, LPVOID param) 01079 { 01080 MSIPACKAGE *package = param; 01081 LPCWSTR propName, sigName; 01082 LPWSTR value = NULL; 01083 MSISIGNATURE sig; 01084 MSIRECORD *uirow; 01085 UINT r; 01086 01087 /* get property and signature */ 01088 propName = MSI_RecordGetString(row, 1); 01089 sigName = MSI_RecordGetString(row, 2); 01090 01091 TRACE("%s %s\n", debugstr_w(propName), debugstr_w(sigName)); 01092 01093 r = ACTION_AppSearchSigName(package, sigName, &sig, &value); 01094 if (value) 01095 { 01096 r = msi_set_property( package->db, propName, value ); 01097 if (r == ERROR_SUCCESS && !strcmpW( propName, szSourceDir )) 01098 msi_reset_folders( package, TRUE ); 01099 01100 msi_free(value); 01101 } 01102 ACTION_FreeSignature(&sig); 01103 01104 uirow = MSI_CreateRecord( 2 ); 01105 MSI_RecordSetStringW( uirow, 1, propName ); 01106 MSI_RecordSetStringW( uirow, 2, sigName ); 01107 msi_ui_actiondata( package, szAppSearch, uirow ); 01108 msiobj_release( &uirow->hdr ); 01109 01110 return r; 01111 } 01112 01113 UINT ACTION_AppSearch(MSIPACKAGE *package) 01114 { 01115 static const WCHAR query[] = { 01116 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01117 'A','p','p','S','e','a','r','c','h',0}; 01118 MSIQUERY *view; 01119 UINT r; 01120 01121 if (msi_action_is_unique(package, szAppSearch)) 01122 { 01123 TRACE("Skipping AppSearch action: already done in UI sequence\n"); 01124 return ERROR_SUCCESS; 01125 } 01126 else 01127 msi_register_unique_action(package, szAppSearch); 01128 01129 r = MSI_OpenQuery( package->db, &view, query ); 01130 if (r != ERROR_SUCCESS) 01131 return ERROR_SUCCESS; 01132 01133 r = MSI_IterateRecords( view, NULL, iterate_appsearch, package ); 01134 msiobj_release( &view->hdr ); 01135 return r; 01136 } 01137 01138 static UINT ITERATE_CCPSearch(MSIRECORD *row, LPVOID param) 01139 { 01140 MSIPACKAGE *package = param; 01141 LPCWSTR signature; 01142 LPWSTR value = NULL; 01143 MSISIGNATURE sig; 01144 UINT r = ERROR_SUCCESS; 01145 01146 static const WCHAR success[] = {'C','C','P','_','S','u','c','c','e','s','s',0}; 01147 01148 signature = MSI_RecordGetString(row, 1); 01149 01150 TRACE("%s\n", debugstr_w(signature)); 01151 01152 ACTION_AppSearchSigName(package, signature, &sig, &value); 01153 if (value) 01154 { 01155 TRACE("Found signature %s\n", debugstr_w(signature)); 01156 msi_set_property(package->db, success, szOne); 01157 msi_free(value); 01158 r = ERROR_NO_MORE_ITEMS; 01159 } 01160 01161 ACTION_FreeSignature(&sig); 01162 01163 return r; 01164 } 01165 01166 UINT ACTION_CCPSearch(MSIPACKAGE *package) 01167 { 01168 static const WCHAR query[] = { 01169 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01170 'C','C','P','S','e','a','r','c','h',0}; 01171 MSIQUERY *view; 01172 UINT r; 01173 01174 if (msi_action_is_unique(package, szCCPSearch)) 01175 { 01176 TRACE("Skipping AppSearch action: already done in UI sequence\n"); 01177 return ERROR_SUCCESS; 01178 } 01179 else 01180 msi_register_unique_action(package, szCCPSearch); 01181 01182 r = MSI_OpenQuery(package->db, &view, query); 01183 if (r != ERROR_SUCCESS) 01184 return ERROR_SUCCESS; 01185 01186 r = MSI_IterateRecords(view, NULL, ITERATE_CCPSearch, package); 01187 msiobj_release(&view->hdr); 01188 return r; 01189 } Generated on Sun May 27 2012 04:25:11 for ReactOS by
1.7.6.1
|