ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

appsearch.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, &regType, 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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.