CVS: winex/dlls/msi .cvsignore, NONE, 1.1 action.c, NONE, 1.1 alter.c, NONE, 1.1 appsearch.c, NONE, 1.1 automation.c, NONE, 1.1 classes.c, NONE, 1.1 cond.y, NONE, 1.1 create.c, NONE, 1.1 custom.c, NONE, 1.1 database.c, NONE, 1.1 delete.c, NONE, 1.1 dialog.c, NONE, 1.1 distinct.c, NONE, 1.1 events.c, NONE, 1.1 files.c, NONE, 1.1 font.c, NONE, 1.1 format.c, NONE, 1.1 handle.c, NONE, 1.1 helpers.c, NONE, 1.1 insert.c, NONE, 1.1 install.c, NONE, 1.1 join.c, NONE, 1.1 Makefile.in, NONE, 1.1 msi.c, NONE, 1.1 msi.rc, NONE, 1.1 msi.spec, NONE, 1.1 msipriv.h, NONE, 1.1 msiquery.c, NONE, 1.1 msiserver.idl, NONE, 1.1 msiserver_dispids.h, NONE, 1.1 msi_Bg.rc, NONE, 1.1 msi_De.rc, NONE, 1.1 msi_En.rc, NONE, 1.1 msi_Eo.rc, NONE, 1.1 msi_Es.rc, NONE, 1.1 msi_Fi.rc, NONE, 1.1 msi_Fr.rc, NONE, 1.1 msi_Hu.rc, NONE, 1.1 msi_It.rc, NONE, 1.1 msi_Ko.rc, NONE, 1.1 msi_main.c, NONE, 1.1
[email protected] 14 Dec 2007 20:40:11 -0000
| Newsgroups | gmane.comp.emulators.winex.cvs |
|---|---|
| Message-ID | <[email protected]> |
Subject: winex/dlls/msi .cvsignore,NONE,1.1 action.c,NONE,1.1 alter.c,NONE,1.1 appsearch.c,NONE,1.1 automation.c,NONE,1.1 classes.c,NONE,1.1 cond.y,NONE,1.1 create.c,NONE,1.1 custom.c,NONE,1.1 database.c,NONE,1.1 delete.c,NONE,1.1 dialog.c,NONE,1.1 distinct.c,NONE,1.1 events.c,NONE,1.1 files.c,NONE,1.1 font.c,NONE,1.1 format.c,NONE,1.1 handle.c,NONE,1.1 helpers.c,NONE,1.1 insert.c,NONE,1.1 install.c,NONE,1.1 join.c,NONE,1.1 Makefile.in,NONE,1.1 msi.c,NONE,1.1 msi.rc,NONE,1.1 msi.spec,NONE,1.1 msipriv.h,NONE,1.1 msiquery.c,NONE,1.1 msiserver.idl,NONE,1.1 msiserver_dispids.h,NONE,1.1 msi_Bg.rc,NONE,1.1 msi_De.rc,NONE,1.1 msi_En.rc,NONE,1.1 msi_Eo.rc,NONE,1.1 msi_Es.rc,NONE,1.1 msi_Fi.rc,NONE,1.1 msi_Fr.rc,NONE,1.1 msi_Hu.rc,NONE,1.1 msi_It.rc,NONE,1.1 msi_Ko.rc,NONE,1.1 msi_main.c,NONE,1.1 m
si_Nl.rc,NONE,1.1 msi_No.rc,NONE,1.1 msi_Pl.rc,NONE,1.1 msi_Pt.rc,NONE,1.1 msi_Ru.rc,NONE,1.1 msi_Tr.rc,NONE,1.1 order.c,NONE,1.1 package.c,NONE,1.1 preview.c,NONE,1.1 query.h,NONE,1.1 record.c,NONE,1.1 registry.c,NONE,1.1 regsvr.c,NONE,1.1 script.c,NONE,1.1 select.c,NONE,1.1 source.c,NONE,1.1 sql.y,NONE,1.1 streams.c,NONE,1.1 string.c,NONE,1.1 suminfo.c,NONE,1.1 table.c,NONE,1.1 tokenize.c,NONE,1.1 update.c,NONE,1.1 upgrade.c,NONE,1.1 version.rc,NONE,1.1 where.c,NONE,1.1Update of /var/lib/cvsd/cvsroot/winex/dlls/msi
In directory agravaine:/tmp/cvs-serv17972/dlls/msi
Added Files:
.cvsignore action.c alter.c appsearch.c automation.c classes.c
cond.y create.c custom.c database.c delete.c dialog.c
distinct.c events.c files.c font.c format.c handle.c helpers.c
insert.c install.c join.c Makefile.in msi.c msi.rc msi.spec
msipriv.h msiquery.c msiserver.idl msiserver_dispids.h
msi_Bg.rc msi_De.rc msi_En.rc msi_Eo.rc msi_Es.rc msi_Fi.rc
msi_Fr.rc msi_Hu.rc msi_It.rc msi_Ko.rc msi_main.c msi_Nl.rc
msi_No.rc msi_Pl.rc msi_Pt.rc msi_Ru.rc msi_Tr.rc order.c
package.c preview.c query.h record.c registry.c regsvr.c
script.c select.c source.c sql.y streams.c string.c suminfo.c
table.c tokenize.c update.c upgrade.c version.rc where.c
Log Message:
Imported MSI.dll from Wine, with minor changes for compilation.
--- NEW FILE: .cvsignore ---
Makefile
cond.tab.c
cond.tab.h
msi.res
msiserver.h
msiserver.tlb
msiserver_i.c
sql.tab.c
sql.tab.h
msi.spec.c
*.d
--- NEW FILE: action.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2004,2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...5188 lines suppressed...]
{ szSelfRegModules, ACTION_SelfRegModules },
{ szSelfUnregModules, ACTION_SelfUnregModules },
{ szSetODBCFolders, NULL },
{ szStartServices, ACTION_StartServices },
{ szStopServices, ACTION_StopServices },
{ szUnpublishComponents, ACTION_UnpublishComponents },
{ szUnpublishFeatures, ACTION_UnpublishFeatures },
{ szUnregisterClassInfo, ACTION_UnregisterClassInfo },
{ szUnregisterComPlus, ACTION_UnregisterComPlus },
{ szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
{ szUnregisterFonts, ACTION_UnregisterFonts },
{ szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
{ szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
{ szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
{ szValidateProductID, ACTION_ValidateProductID },
{ szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
{ szWriteIniValues, ACTION_WriteIniValues },
{ szWriteRegistryValues, ACTION_WriteRegistryValues },
{ NULL, NULL },
};
--- NEW FILE: alter.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2006 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
typedef struct tagMSIALTERVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
column_info *colinfo;
INT hold;
} MSIALTERVIEW;
static UINT ALTER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %d %d %p\n", av, row, col, val );
return ERROR_FUNCTION_FAILED;
}
static UINT ALTER_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %d %d %p\n", av, row, col, stm );
return ERROR_FUNCTION_FAILED;
}
static UINT ITERATE_columns(MSIRECORD *row, LPVOID param)
{
(*(UINT *)param)++;
return ERROR_SUCCESS;
}
static BOOL check_column_exists(MSIDATABASE *db, MSIVIEW *columns, LPCWSTR table, LPCWSTR column)
{
MSIQUERY *view;
MSIRECORD *rec;
UINT r;
static const WCHAR query[] = {
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
'`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ',
'`','T','a','b','l','e','`','=','\'','%','s','\'',' ','A','N','D',' ',
'`','N','a','m','e','`','=','\'','%','s','\'',0
};
r = MSI_OpenQuery(db, &view, query, table, column);
if (r != ERROR_SUCCESS)
return FALSE;
r = MSI_ViewExecute(view, NULL);
if (r != ERROR_SUCCESS)
goto done;
r = MSI_ViewFetch(view, &rec);
if (r == ERROR_SUCCESS)
msiobj_release(&rec->hdr);
done:
msiobj_release(&view->hdr);
return (r == ERROR_SUCCESS);
}
static UINT alter_add_column(MSIALTERVIEW *av)
{
UINT r, colnum = 1;
MSIQUERY *view;
MSIVIEW *columns;
static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0};
static const WCHAR query[] = {
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
'`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ',
'`','T','a','b','l','e','`','=','\'','%','s','\'',' ','O','R','D','E','R',' ',
'B','Y',' ','`','N','u','m','b','e','r','`',0
};
r = TABLE_CreateView(av->db, szColumns, &columns);
if (r != ERROR_SUCCESS)
return r;
if (check_column_exists(av->db, columns, av->colinfo->table, av->colinfo->column))
return ERROR_BAD_QUERY_SYNTAX;
r = MSI_OpenQuery(av->db, &view, query, av->colinfo->table, av->colinfo->column);
if (r == ERROR_SUCCESS)
{
r = MSI_IterateRecords(view, NULL, ITERATE_columns, &colnum);
msiobj_release(&view->hdr);
}
r = columns->ops->add_column(columns, av->colinfo->table,
colnum, av->colinfo->column,
av->colinfo->type, (av->hold == 1));
columns->ops->delete(columns);
return r;
}
static UINT ALTER_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %p\n", av, record);
if (av->hold == 1)
av->table->ops->add_ref(av->table);
else if (av->hold == -1)
av->table->ops->release(av->table);
if (av->colinfo)
return alter_add_column(av);
return ERROR_SUCCESS;
}
static UINT ALTER_close( struct tagMSIVIEW *view )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p\n", av );
return ERROR_SUCCESS;
}
static UINT ALTER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %p %p\n", av, rows, cols );
return ERROR_FUNCTION_FAILED;
}
static UINT ALTER_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %d %p %p\n", av, n, name, type );
return ERROR_FUNCTION_FAILED;
}
static UINT ALTER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p %d %p\n", av, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT ALTER_delete( struct tagMSIVIEW *view )
{
MSIALTERVIEW *av = (MSIALTERVIEW*)view;
TRACE("%p\n", av );
msi_free( av );
av->table->ops->delete( av->table );
return ERROR_SUCCESS;
}
static UINT ALTER_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
return ERROR_FUNCTION_FAILED;
}
static const MSIVIEWOPS alter_ops =
{
ALTER_fetch_int,
ALTER_fetch_stream,
NULL,
NULL,
NULL,
ALTER_execute,
ALTER_close,
ALTER_get_dimensions,
ALTER_get_column_info,
ALTER_modify,
ALTER_delete,
ALTER_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold )
{
MSIALTERVIEW *av;
UINT r;
TRACE("%p %p %s %d\n", view, colinfo, debugstr_w(name), hold );
av = msi_alloc_zero( sizeof *av );
if( !av )
return ERROR_FUNCTION_FAILED;
r = TABLE_CreateView( db, name, &av->table );
if (r != ERROR_SUCCESS || !av->table)
return r;
if (colinfo)
colinfo->table = name;
/* fill the structure */
av->view.ops = &alter_ops;
av->db = db;
av->hold = hold;
av->colinfo = colinfo;
*view = &av->view;
return ERROR_SUCCESS;
}
--- NEW FILE: appsearch.c ---
/*
* Implementation of the AppSearch action of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Juan Lang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "wine-lgpl/msidefs.h"
#include "winver.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "msipriv.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
typedef struct tagMSISIGNATURE
{
LPCWSTR Name; /* NOT owned by this structure */
LPWSTR File;
DWORD MinVersionMS;
DWORD MinVersionLS;
DWORD MaxVersionMS;
DWORD MaxVersionLS;
DWORD MinSize;
DWORD MaxSize;
FILETIME MinTime;
FILETIME MaxTime;
LPWSTR Languages;
}MSISIGNATURE;
static void ACTION_VerStrToInteger(LPCWSTR verStr, PDWORD ms, PDWORD ls)
{
const WCHAR *ptr;
int x1 = 0, x2 = 0, x3 = 0, x4 = 0;
x1 = atoiW(verStr);
ptr = strchrW(verStr, '.');
if (ptr)
{
x2 = atoiW(ptr + 1);
ptr = strchrW(ptr + 1, '.');
}
if (ptr)
{
x3 = atoiW(ptr + 1);
ptr = strchrW(ptr + 1, '.');
}
if (ptr)
x4 = atoiW(ptr + 1);
/* FIXME: byte-order dependent? */
*ms = x1 << 16 | x2;
*ls = x3 << 16 | x4;
}
/* Fills in sig with the the values from the Signature table, where name is the
* signature to find. Upon return, sig->File will be NULL if the record is not
* found, and not NULL if it is found.
* Warning: clears all fields in sig!
* Returns ERROR_SUCCESS upon success (where not finding the record counts as
* success), something else on error.
*/
static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, LPCWSTR name)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'S','i','g','n','a','t','u','r','e',' ',
'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ',
'\'','%','s','\'',0};
LPWSTR minVersion, maxVersion;
MSIRECORD *row;
DWORD time;
TRACE("package %p, sig %p\n", package, sig);
memset(sig, 0, sizeof(*sig));
sig->Name = name;
row = MSI_QueryGetRecord( package->db, query, name );
if (!row)
{
TRACE("failed to query signature for %s\n", debugstr_w(name));
return ERROR_SUCCESS;
}
/* get properties */
sig->File = msi_dup_record_field(row,2);
minVersion = msi_dup_record_field(row,3);
if (minVersion)
{
ACTION_VerStrToInteger(minVersion, &sig->MinVersionMS, &sig->MinVersionLS);
msi_free( minVersion );
}
maxVersion = msi_dup_record_field(row,4);
if (maxVersion)
{
ACTION_VerStrToInteger(maxVersion, &sig->MaxVersionMS, &sig->MaxVersionLS);
msi_free( maxVersion );
}
sig->MinSize = MSI_RecordGetInteger(row,5);
if (sig->MinSize == MSI_NULL_INTEGER)
sig->MinSize = 0;
sig->MaxSize = MSI_RecordGetInteger(row,6);
if (sig->MaxSize == MSI_NULL_INTEGER)
sig->MaxSize = 0;
sig->Languages = msi_dup_record_field(row,9);
time = MSI_RecordGetInteger(row,7);
if (time != MSI_NULL_INTEGER)
DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime);
time = MSI_RecordGetInteger(row,8);
if (time != MSI_NULL_INTEGER)
DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime);
TRACE("Found file name %s for Signature_ %s;\n",
debugstr_w(sig->File), debugstr_w(name));
TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS),
LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS),
LOWORD(sig->MinVersionLS));
TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS),
LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS),
LOWORD(sig->MaxVersionLS));
TRACE("MinSize is %d, MaxSize is %d;\n", sig->MinSize, sig->MaxSize);
TRACE("Languages is %s\n", debugstr_w(sig->Languages));
msiobj_release( &row->hdr );
return ERROR_SUCCESS;
}
/* Frees any memory allocated in sig */
static void ACTION_FreeSignature(MSISIGNATURE *sig)
{
msi_free(sig->File);
msi_free(sig->Languages);
}
static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'C','o','m','p','L','o','c','a','t','o','r',' ',
'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
'\'','%','s','\'',0};
MSIRECORD *row;
LPWSTR guid;
TRACE("%s\n", debugstr_w(sig->Name));
*appValue = NULL;
row = MSI_QueryGetRecord( package->db, query, sig->Name );
if (!row)
{
TRACE("failed to query CompLocator for %s\n", debugstr_w(sig->Name));
return ERROR_SUCCESS;
}
guid = msi_dup_record_field( row, 2 );
FIXME("AppSearch CompLocator (%s) unimplemented\n", debugstr_w(guid));
msi_free( guid );
msiobj_release( &row->hdr );
return ERROR_SUCCESS;
}
static void ACTION_ConvertRegValue(DWORD regType, const BYTE *value, DWORD sz,
LPWSTR *appValue)
{
static const WCHAR dwordFmt[] = { '#','%','d','\0' };
static const WCHAR expandSzFmt[] = { '#','%','%','%','s','\0' };
static const WCHAR binFmt[] = { '#','x','%','x','\0' };
DWORD i;
switch (regType)
{
case REG_SZ:
if (*(LPCWSTR)value == '#')
{
/* escape leading pound with another */
*appValue = msi_alloc(sz + sizeof(WCHAR));
(*appValue)[0] = '#';
strcpyW(*appValue + 1, (LPCWSTR)value);
}
else
{
*appValue = msi_alloc(sz);
strcpyW(*appValue, (LPCWSTR)value);
}
break;
case REG_DWORD:
/* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign
* char if needed
*/
*appValue = msi_alloc(10 * sizeof(WCHAR));
sprintfW(*appValue, dwordFmt, *(const DWORD *)value);
break;
case REG_EXPAND_SZ:
/* space for extra #% characters in front */
*appValue = msi_alloc(sz + 2 * sizeof(WCHAR));
sprintfW(*appValue, expandSzFmt, (LPCWSTR)value);
break;
case REG_BINARY:
/* 3 == length of "#x<nibble>" */
*appValue = msi_alloc((sz * 3 + 1) * sizeof(WCHAR));
for (i = 0; i < sz; i++)
sprintfW(*appValue + i * 3, binFmt, value[i]);
break;
default:
WARN("unimplemented for values of type %d\n", regType);
*appValue = NULL;
}
}
static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
LPCWSTR path, int depth, LPWSTR *appValue);
static UINT ACTION_AppSearchReg(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'R','e','g','L','o','c','a','t','o','r',' ',
'w','h','e','r','e',' ',
'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0};
LPWSTR keyPath = NULL, valueName = NULL;
LPWSTR deformatted = NULL;
int root, type;
HKEY rootKey, key = NULL;
DWORD sz = 0, regType;
LPBYTE value = NULL;
MSIRECORD *row;
UINT rc;
TRACE("%s\n", debugstr_w(sig->Name));
*appValue = NULL;
row = MSI_QueryGetRecord( package->db, query, sig->Name );
if (!row)
{
TRACE("failed to query RegLocator for %s\n", debugstr_w(sig->Name));
return ERROR_SUCCESS;
}
root = MSI_RecordGetInteger(row,2);
keyPath = msi_dup_record_field(row,3);
valueName = msi_dup_record_field(row,4);
type = MSI_RecordGetInteger(row,5);
deformat_string(package, keyPath, &deformatted);
switch (root)
{
case msidbRegistryRootClassesRoot:
rootKey = HKEY_CLASSES_ROOT;
break;
case msidbRegistryRootCurrentUser:
rootKey = HKEY_CURRENT_USER;
break;
case msidbRegistryRootLocalMachine:
rootKey = HKEY_LOCAL_MACHINE;
break;
case msidbRegistryRootUsers:
rootKey = HKEY_USERS;
break;
default:
WARN("Unknown root key %d\n", root);
goto end;
}
rc = RegOpenKeyW(rootKey, deformatted, &key);
if (rc)
{
TRACE("RegOpenKeyW returned %d\n", rc);
goto end;
}
rc = RegQueryValueExW(key, valueName, NULL, NULL, NULL, &sz);
if (rc)
{
TRACE("RegQueryValueExW returned %d\n", rc);
goto end;
}
/* FIXME: sanity-check sz before allocating (is there an upper-limit
* on the value of a property?)
*/
value = msi_alloc( sz );
rc = RegQueryValueExW(key, valueName, NULL, ®Type, value, &sz);
if (rc)
{
TRACE("RegQueryValueExW returned %d\n", rc);
goto end;
}
/* bail out if the registry key is empty */
if (sz == 0)
goto end;
switch (type & 0x0f)
{
case msidbLocatorTypeDirectory:
rc = ACTION_SearchDirectory(package, sig, (LPWSTR)value, 0, appValue);
break;
case msidbLocatorTypeFileName:
*appValue = strdupW((LPWSTR)value);
break;
case msidbLocatorTypeRawValue:
ACTION_ConvertRegValue(regType, value, sz, appValue);
break;
default:
FIXME("AppSearch unimplemented for type %d (key path %s, value %s)\n",
type, debugstr_w(keyPath), debugstr_w(valueName));
}
end:
msi_free( value );
RegCloseKey( key );
msi_free( keyPath );
msi_free( valueName );
msi_free( deformatted );
msiobj_release(&row->hdr);
return ERROR_SUCCESS;
}
static UINT ACTION_AppSearchIni(MSIPACKAGE *package, LPWSTR *appValue,
MSISIGNATURE *sig)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'I','n','i','L','o','c','a','t','o','r',' ',
'w','h','e','r','e',' ',
'S','i','g','n','a','t','u','r','e','_',' ','=',' ','\'','%','s','\'',0};
MSIRECORD *row;
LPWSTR fileName, section, key;
int field, type;
WCHAR buf[MAX_PATH];
TRACE("%s\n", debugstr_w(sig->Name));
*appValue = NULL;
row = MSI_QueryGetRecord( package->db, query, sig->Name );
if (!row)
{
TRACE("failed to query IniLocator for %s\n", debugstr_w(sig->Name));
return ERROR_SUCCESS;
}
fileName = msi_dup_record_field(row, 2);
section = msi_dup_record_field(row, 3);
key = msi_dup_record_field(row, 4);
field = MSI_RecordGetInteger(row, 5);
type = MSI_RecordGetInteger(row, 6);
if (field == MSI_NULL_INTEGER)
field = 0;
if (type == MSI_NULL_INTEGER)
type = 0;
GetPrivateProfileStringW(section, key, NULL, buf, MAX_PATH, fileName);
if (buf[0])
{
switch (type & 0x0f)
{
case msidbLocatorTypeDirectory:
FIXME("unimplemented for Directory (%s)\n", debugstr_w(buf));
break;
case msidbLocatorTypeFileName:
FIXME("unimplemented for File (%s)\n", debugstr_w(buf));
break;
case msidbLocatorTypeRawValue:
*appValue = strdupW(buf);
break;
}
}
msi_free(fileName);
msi_free(section);
msi_free(key);
msiobj_release(&row->hdr);
return ERROR_SUCCESS;
}
/* Expands the value in src into a path without property names and only
* containing long path names into dst. Replaces at most len characters of dst,
* and always NULL-terminates dst if dst is not NULL and len >= 1.
* May modify src.
* Assumes src and dst are non-overlapping.
* FIXME: return code probably needed:
* - what does AppSearch return if the table values are invalid?
* - what if dst is too small?
*/
static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst,
size_t len)
{
WCHAR *ptr;
size_t copied = 0;
if (!src || !dst || !len)
{
if (dst) *dst = '\0';
return;
}
/* Ignore the short portion of the path, don't think we can use it anyway */
if ((ptr = strchrW(src, '|')))
ptr++;
else
ptr = src;
while (*ptr && copied < len - 1)
{
WCHAR *prop = strchrW(ptr, '[');
if (prop)
{
WCHAR *propEnd = strchrW(prop + 1, ']');
if (!propEnd)
{
WARN("Unterminated property name in AnyPath: %s\n",
debugstr_w(prop));
break;
}
else
{
DWORD propLen;
*propEnd = 0;
propLen = len - copied - 1;
MSI_GetPropertyW(package, prop + 1, dst + copied, &propLen);
ptr = propEnd + 1;
copied += propLen;
}
}
else
{
size_t toCopy = min(strlenW(ptr) + 1, len - copied - 1);
memcpy(dst + copied, ptr, toCopy * sizeof(WCHAR));
ptr += toCopy;
copied += toCopy;
}
}
*(dst + copied) = '\0';
}
/* Sets *matches to whether the file (whose path is filePath) matches the
* versions set in sig.
* Return ERROR_SUCCESS in case of success (whether or not the file matches),
* something else if an install-halting error occurs.
*/
static UINT ACTION_FileVersionMatches(const MSISIGNATURE *sig, LPCWSTR filePath,
BOOL *matches)
{
UINT rc = ERROR_SUCCESS;
*matches = FALSE;
if (sig->Languages)
{
FIXME(": need to check version for languages %s\n",
debugstr_w(sig->Languages));
}
else
{
DWORD zero, size = GetFileVersionInfoSizeW(filePath, &zero);
if (size)
{
LPVOID buf = msi_alloc( size);
if (buf)
{
static const WCHAR rootW[] = { '\\',0 };
UINT versionLen;
LPVOID subBlock = NULL;
if (GetFileVersionInfoW(filePath, 0, size, buf))
VerQueryValueW(buf, rootW, &subBlock, &versionLen);
if (subBlock)
{
VS_FIXEDFILEINFO *info =
(VS_FIXEDFILEINFO *)subBlock;
TRACE("Comparing file version %d.%d.%d.%d:\n",
HIWORD(info->dwFileVersionMS),
LOWORD(info->dwFileVersionMS),
HIWORD(info->dwFileVersionLS),
LOWORD(info->dwFileVersionLS));
if (info->dwFileVersionMS < sig->MinVersionMS
|| (info->dwFileVersionMS == sig->MinVersionMS &&
info->dwFileVersionLS < sig->MinVersionLS))
{
TRACE("Less than minimum version %d.%d.%d.%d\n",
HIWORD(sig->MinVersionMS),
LOWORD(sig->MinVersionMS),
HIWORD(sig->MinVersionLS),
LOWORD(sig->MinVersionLS));
}
else if (info->dwFileVersionMS < sig->MinVersionMS
|| (info->dwFileVersionMS == sig->MinVersionMS &&
info->dwFileVersionLS < sig->MinVersionLS))
{
TRACE("Greater than minimum version %d.%d.%d.%d\n",
HIWORD(sig->MaxVersionMS),
LOWORD(sig->MaxVersionMS),
HIWORD(sig->MaxVersionLS),
LOWORD(sig->MaxVersionLS));
}
else
*matches = TRUE;
}
msi_free( buf);
}
else
rc = ERROR_OUTOFMEMORY;
}
}
return rc;
}
/* Sets *matches to whether the file in findData matches that in sig.
* fullFilePath is assumed to be the full path of the file specified in
* findData, which may be necessary to compare the version.
* Return ERROR_SUCCESS in case of success (whether or not the file matches),
* something else if an install-halting error occurs.
*/
static UINT ACTION_FileMatchesSig(const MSISIGNATURE *sig,
const WIN32_FIND_DATAW *findData, LPCWSTR fullFilePath, BOOL *matches)
{
UINT rc = ERROR_SUCCESS;
*matches = TRUE;
/* assumes the caller has already ensured the filenames match, so check
* the other fields..
*/
if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime)
{
if (findData->ftCreationTime.dwHighDateTime <
sig->MinTime.dwHighDateTime ||
(findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime
&& findData->ftCreationTime.dwLowDateTime <
sig->MinTime.dwLowDateTime))
*matches = FALSE;
}
if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime))
{
if (findData->ftCreationTime.dwHighDateTime >
sig->MaxTime.dwHighDateTime ||
(findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime
&& findData->ftCreationTime.dwLowDateTime >
sig->MaxTime.dwLowDateTime))
*matches = FALSE;
}
if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize)
*matches = FALSE;
if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize)
*matches = FALSE;
if (*matches && (sig->MinVersionMS || sig->MinVersionLS ||
sig->MaxVersionMS || sig->MaxVersionLS))
rc = ACTION_FileVersionMatches(sig, fullFilePath, matches);
return rc;
}
/* Recursively searches the directory dir for files that match the signature
* sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir
* (and only dir). If depth is 1, searches dir and its immediate
* subdirectories.
* Assumes sig->File is not NULL.
* Returns ERROR_SUCCESS on success (which may include non-critical errors),
* something else on failures which should halt the install.
*/
static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, LPWSTR *appValue,
MSISIGNATURE *sig, LPCWSTR dir, int depth)
{
static const WCHAR starDotStarW[] = { '*','.','*',0 };
UINT rc = ERROR_SUCCESS;
size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File);
WCHAR *buf;
TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir),
debugstr_w(sig->File), depth);
if (depth < 0)
return ERROR_INVALID_PARAMETER;
*appValue = NULL;
/* We need the buffer in both paths below, so go ahead and allocate it
* here. Add two because we might need to add a backslash if the dir name
* isn't backslash-terminated.
*/
buf = msi_alloc( (dirLen + max(fileLen, lstrlenW(starDotStarW)) + 2) * sizeof(WCHAR));
if (buf)
{
/* a depth of 0 implies we should search dir, so go ahead and search */
HANDLE hFind;
WIN32_FIND_DATAW findData;
memcpy(buf, dir, dirLen * sizeof(WCHAR));
if (buf[dirLen - 1] != '\\')
buf[dirLen++ - 1] = '\\';
memcpy(buf + dirLen, sig->File, (fileLen + 1) * sizeof(WCHAR));
hFind = FindFirstFileW(buf, &findData);
if (hFind != INVALID_HANDLE_VALUE)
{
BOOL matches;
/* assuming Signature can't contain wildcards for the file name,
* so don't bother with FindNextFileW here.
*/
if (!(rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches))
&& matches)
{
TRACE("found file, returning %s\n", debugstr_w(buf));
*appValue = buf;
}
FindClose(hFind);
}
if (rc == ERROR_SUCCESS && !*appValue && depth > 0)
{
HANDLE hFind;
WIN32_FIND_DATAW findData;
memcpy(buf, dir, dirLen * sizeof(WCHAR));
if (buf[dirLen - 1] != '\\')
buf[dirLen++ - 1] = '\\';
lstrcpyW(buf + dirLen, starDotStarW);
hFind = FindFirstFileW(buf, &findData);
if (hFind != INVALID_HANDLE_VALUE)
{
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
rc = ACTION_RecurseSearchDirectory(package, appValue, sig,
findData.cFileName, depth - 1);
while (rc == ERROR_SUCCESS && !*appValue &&
FindNextFileW(hFind, &findData) != 0)
{
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
rc = ACTION_RecurseSearchDirectory(package, appValue,
sig, findData.cFileName, depth - 1);
}
FindClose(hFind);
}
}
if (!*appValue)
msi_free(buf);
}
else
rc = ERROR_OUTOFMEMORY;
return rc;
}
static UINT ACTION_CheckDirectory(MSIPACKAGE *package, LPCWSTR dir,
LPWSTR *appValue)
{
UINT rc = ERROR_SUCCESS;
if (GetFileAttributesW(dir) & FILE_ATTRIBUTE_DIRECTORY)
{
TRACE("directory exists, returning %s\n", debugstr_w(dir));
*appValue = strdupW(dir);
}
return rc;
}
static BOOL ACTION_IsFullPath(LPCWSTR path)
{
WCHAR first = toupperW(path[0]);
BOOL ret;
if (first >= 'A' && first <= 'Z' && path[1] == ':')
ret = TRUE;
else if (path[0] == '\\' && path[1] == '\\')
ret = TRUE;
else
ret = FALSE;
return ret;
}
static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
LPCWSTR path, int depth, LPWSTR *appValue)
{
UINT rc;
TRACE("%p, %p, %s, %d, %p\n", package, sig, debugstr_w(path), depth,
appValue);
if (ACTION_IsFullPath(path))
{
if (sig->File)
rc = ACTION_RecurseSearchDirectory(package, appValue, sig,
path, depth);
else
{
/* Recursively searching a directory makes no sense when the
* directory to search is the thing you're trying to find.
*/
rc = ACTION_CheckDirectory(package, path, appValue);
}
}
else
{
WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 };
DWORD drives = GetLogicalDrives();
int i;
rc = ERROR_SUCCESS;
*appValue = NULL;
for (i = 0; rc == ERROR_SUCCESS && !*appValue && i < 26; i++)
if (drives & (1 << drives))
{
pathWithDrive[0] = 'A' + i;
if (GetDriveTypeW(pathWithDrive) == DRIVE_FIXED)
{
lstrcpynW(pathWithDrive + 3, path,
sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3);
if (sig->File)
rc = ACTION_RecurseSearchDirectory(package, appValue,
sig, pathWithDrive, depth);
else
rc = ACTION_CheckDirectory(package, pathWithDrive,
appValue);
}
}
}
TRACE("returning %d\n", rc);
return rc;
}
static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName,
MSISIGNATURE *sig, LPWSTR *appValue);
static UINT ACTION_AppSearchDr(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'D','r','L','o','c','a','t','o','r',' ',
'w','h','e','r','e',' ',
'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0};
LPWSTR parentName = NULL, path = NULL, parent = NULL;
WCHAR expanded[MAX_PATH];
MSIRECORD *row;
int depth;
UINT rc;
TRACE("%s\n", debugstr_w(sig->Name));
*appValue = NULL;
row = MSI_QueryGetRecord( package->db, query, sig->Name );
if (!row)
{
TRACE("failed to query DrLocator for %s\n", debugstr_w(sig->Name));
return ERROR_SUCCESS;
}
/* check whether parent is set */
parentName = msi_dup_record_field(row,2);
if (parentName)
{
MSISIGNATURE parentSig;
rc = ACTION_AppSearchSigName(package, parentName, &parentSig, &parent);
ACTION_FreeSignature(&parentSig);
msi_free(parentName);
}
/* now look for path */
path = msi_dup_record_field(row,3);
if (MSI_RecordIsNull(row,4))
depth = 0;
else
depth = MSI_RecordGetInteger(row,4);
ACTION_ExpandAnyPath(package, path, expanded, MAX_PATH);
msi_free(path);
if (parent)
{
path = msi_alloc((strlenW(parent) + strlenW(expanded) + 1) * sizeof(WCHAR));
if (!path)
{
rc = ERROR_OUTOFMEMORY;
goto end;
}
strcpyW(path, parent);
strcatW(path, expanded);
}
else
path = expanded;
rc = ACTION_SearchDirectory(package, sig, path, depth, appValue);
end:
if (path != expanded)
msi_free(path);
msi_free(parent);
msiobj_release(&row->hdr);
TRACE("returning %d\n", rc);
return rc;
}
static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName,
MSISIGNATURE *sig, LPWSTR *appValue)
{
UINT rc;
*appValue = NULL;
rc = ACTION_AppSearchGetSignature(package, sig, sigName);
if (rc == ERROR_SUCCESS)
{
rc = ACTION_AppSearchComponents(package, appValue, sig);
if (rc == ERROR_SUCCESS && !*appValue)
{
rc = ACTION_AppSearchReg(package, appValue, sig);
if (rc == ERROR_SUCCESS && !*appValue)
{
rc = ACTION_AppSearchIni(package, appValue, sig);
if (rc == ERROR_SUCCESS && !*appValue)
rc = ACTION_AppSearchDr(package, appValue, sig);
}
}
}
return rc;
}
static UINT iterate_appsearch(MSIRECORD *row, LPVOID param)
{
MSIPACKAGE *package = param;
LPWSTR propName, sigName, value = NULL;
MSISIGNATURE sig;
UINT r;
/* get property and signature */
propName = msi_dup_record_field(row,1);
sigName = msi_dup_record_field(row,2);
TRACE("%s %s\n", debugstr_w(propName), debugstr_w(sigName));
r = ACTION_AppSearchSigName(package, sigName, &sig, &value);
if (value)
{
MSI_SetPropertyW(package, propName, value);
msi_free(value);
}
ACTION_FreeSignature(&sig);
msi_free(propName);
msi_free(sigName);
return r;
}
/* http://msdn.microsoft.com/library/en-us/msi/setup/appsearch_table.asp
* is the best reference for the AppSearch table and how it's used.
*/
UINT ACTION_AppSearch(MSIPACKAGE *package)
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ',
'A','p','p','S','e','a','r','c','h',0};
MSIQUERY *view = NULL;
UINT r;
r = MSI_OpenQuery( package->db, &view, query );
if (r != ERROR_SUCCESS)
return ERROR_SUCCESS;
r = MSI_IterateRecords( view, NULL, iterate_appsearch, package );
msiobj_release( &view->hdr );
return r;
}
--- NEW FILE: automation.c ---
/*
* Implementation of OLE Automation for Microsoft Installer (msi.dll)
*
* Copyright 2007 Misha Koshelev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1880 lines suppressed...]
VariantClear(&varg1);
VariantClear(&varg0);
return S_OK;
}
/* Wrapper around create_automation_object to create an installer object. */
HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj)
{
return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke, NULL, 0);
}
/* Wrapper around create_automation_object to create a session object. */
HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch)
{
HRESULT hr = create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke, NULL, sizeof(SessionData));
if (SUCCEEDED(hr) && pDispatch && *pDispatch)
((SessionData *)private_data((AutomationObject *)*pDispatch))->pInstaller = pInstaller;
return hr;
}
--- NEW FILE: classes.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1255 lines suppressed...]
key = msi_alloc( (strlenW(mime)+strlenW(fmt)+1) * sizeof(WCHAR) );
sprintfW(key,fmt,mime);
msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExten, extension );
msi_free(extension);
msi_free(key);
if (mt->clsid)
FIXME("Handle non null for field 3\n");
uirow = MSI_CreateRecord(2);
MSI_RecordSetStringW(uirow,1,mt->ContentType);
MSI_RecordSetStringW(uirow,2,exten);
ui_actiondata(package,szRegisterMIMEInfo,uirow);
msiobj_release(&uirow->hdr);
}
return ERROR_SUCCESS;
}
--- NEW FILE: cond.y ---
%{
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2003 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "oleauto.h"
#include "msipriv.h"
#include "msiserver.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#define YYLEX_PARAM info
#define YYPARSE_PARAM info
static int cond_error(const char *str);
WINE_DEFAULT_DEBUG_CHANNEL(msi);
typedef struct tag_yyinput
{
MSIPACKAGE *package;
LPCWSTR str;
INT n;
MSICONDITION result;
} COND_input;
struct cond_str {
LPCWSTR data;
INT len;
};
static LPWSTR COND_GetString( const struct cond_str *str );
static LPWSTR COND_GetLiteral( const struct cond_str *str );
static int cond_lex( void *COND_lval, COND_input *info);
static const WCHAR szEmpty[] = { 0 };
static INT compare_int( INT a, INT operator, INT b );
static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b );
static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b )
{
INT r;
r = compare_string( a, op, b );
msi_free( a );
msi_free( b );
return r;
}
static BOOL num_from_prop( LPCWSTR p, INT *val )
{
INT ret = 0, sign = 1;
if (!p)
return FALSE;
if (*p == '-')
{
sign = -1;
p++;
}
if (!*p)
return FALSE;
while (*p)
{
if( *p < '0' || *p > '9' )
return FALSE;
ret = ret*10 + (*p - '0');
p++;
}
*val = ret*sign;
return TRUE;
}
%}
%pure-parser
%union
{
struct cond_str str;
LPWSTR string;
INT value;
}
%token COND_SPACE COND_EOF
%token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV
%token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE
%token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE
%token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS
%token COND_ILHS COND_IRHS COND_LHS COND_RHS
%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
%nonassoc COND_ERROR COND_EOF
%type <value> expression boolean_term boolean_factor
%type <value> value_i integer operator
%type <string> identifier symbol_s value_s literal
%%
condition:
expression
{
COND_input* cond = (COND_input*) info;
cond->result = $1;
}
| /* empty */
{
COND_input* cond = (COND_input*) info;
cond->result = MSICONDITION_NONE;
}
;
expression:
boolean_term
{
$$ = $1;
}
| expression COND_OR boolean_term
{
$$ = $1 || $3;
}
| expression COND_IMP boolean_term
{
$$ = !$1 || $3;
}
| expression COND_XOR boolean_term
{
$$ = ( $1 || $3 ) && !( $1 && $3 );
}
| expression COND_EQV boolean_term
{
$$ = ( $1 && $3 ) || ( !$1 && !$3 );
}
;
boolean_term:
boolean_factor
{
$$ = $1;
}
| boolean_term COND_AND boolean_factor
{
$$ = $1 && $3;
}
;
boolean_factor:
COND_NOT boolean_factor
{
$$ = $2 ? 0 : 1;
}
| value_i
{
$$ = $1 ? 1 : 0;
}
| value_s
{
$$ = ($1 && $1[0]) ? 1 : 0;
msi_free($1);
}
| value_i operator value_i
{
$$ = compare_int( $1, $2, $3 );
}
| symbol_s operator value_i
{
int num;
if (num_from_prop( $1, &num ))
$$ = compare_int( num, $2, $3 );
else
$$ = ($2 == COND_NE || $2 == COND_INE );
msi_free($1);
}
| value_i operator symbol_s
{
int num;
if (num_from_prop( $3, &num ))
$$ = compare_int( $1, $2, num );
else
$$ = ($2 == COND_NE || $2 == COND_INE );
msi_free($3);
}
| symbol_s operator symbol_s
{
$$ = compare_and_free_strings( $1, $2, $3 );
}
| symbol_s operator literal
{
$$ = compare_and_free_strings( $1, $2, $3 );
}
| literal operator symbol_s
{
$$ = compare_and_free_strings( $1, $2, $3 );
}
| literal operator literal
{
$$ = compare_and_free_strings( $1, $2, $3 );
}
| literal operator value_i
{
$$ = 0;
msi_free($1);
}
| value_i operator literal
{
$$ = 0;
msi_free($3);
}
| COND_LPAR expression COND_RPAR
{
$$ = $2;
}
;
operator:
/* common functions */
COND_EQ { $$ = COND_EQ; }
| COND_NE { $$ = COND_NE; }
| COND_LT { $$ = COND_LT; }
| COND_GT { $$ = COND_GT; }
| COND_LE { $$ = COND_LE; }
| COND_GE { $$ = COND_GE; }
| COND_SS { $$ = COND_SS; }
| COND_IEQ { $$ = COND_IEQ; }
| COND_INE { $$ = COND_INE; }
| COND_ILT { $$ = COND_ILT; }
| COND_IGT { $$ = COND_IGT; }
| COND_ILE { $$ = COND_ILE; }
| COND_IGE { $$ = COND_IGE; }
| COND_ISS { $$ = COND_ISS; }
| COND_LHS { $$ = COND_LHS; }
| COND_RHS { $$ = COND_RHS; }
| COND_ILHS { $$ = COND_ILHS; }
| COND_IRHS { $$ = COND_IRHS; }
;
value_s:
symbol_s
{
$$ = $1;
}
| literal
{
$$ = $1;
}
;
literal:
COND_LITER
{
$$ = COND_GetLiteral(&$1);
if( !$$ )
YYABORT;
}
;
value_i:
integer
{
$$ = $1;
}
| COND_DOLLARS identifier
{
COND_input* cond = (COND_input*) info;
INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
MSI_GetComponentStateW(cond->package, $2, &install, &action );
$$ = action;
msi_free( $2 );
}
| COND_QUESTION identifier
{
COND_input* cond = (COND_input*) info;
INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
MSI_GetComponentStateW(cond->package, $2, &install, &action );
$$ = install;
msi_free( $2 );
}
| COND_AMPER identifier
{
COND_input* cond = (COND_input*) info;
INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
MSI_GetFeatureStateW(cond->package, $2, &install, &action );
$$ = action;
msi_free( $2 );
}
| COND_EXCLAM identifier
{
COND_input* cond = (COND_input*) info;
INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
MSI_GetFeatureStateW(cond->package, $2, &install, &action );
$$ = install;
msi_free( $2 );
}
;
symbol_s:
identifier
{
COND_input* cond = (COND_input*) info;
$$ = msi_dup_property( cond->package, $1 );
msi_free( $1 );
}
| COND_PERCENT identifier
{
UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
$$ = NULL;
if (len++)
{
$$ = msi_alloc( len*sizeof (WCHAR) );
GetEnvironmentVariableW( $2, $$, len );
}
msi_free( $2 );
}
;
identifier:
COND_IDENT
{
$$ = COND_GetString(&$1);
if( !$$ )
YYABORT;
}
;
integer:
COND_NUMBER
{
LPWSTR szNum = COND_GetString(&$1);
if( !szNum )
YYABORT;
$$ = atoiW( szNum );
msi_free( szNum );
}
;
%%
static int COND_IsAlpha( WCHAR x )
{
return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
( ( x >= 'a' ) && ( x <= 'z' ) ) ||
( ( x == '_' ) ) );
}
static int COND_IsNumber( WCHAR x )
{
return( (( x >= '0' ) && ( x <= '9' )) || (x =='-') || (x =='.') );
}
static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub )
{
LPWSTR strlower, sublower, r;
strlower = CharLowerW( strdupW( str ) );
sublower = CharLowerW( strdupW( sub ) );
r = strstrW( strlower, sublower );
if (r)
r = (LPWSTR)str + (r - strlower);
msi_free( strlower );
msi_free( sublower );
return r;
}
static BOOL str_is_number( LPCWSTR str )
{
int i;
for (i = 0; i < lstrlenW( str ); i++)
if (!isdigitW(str[i]))
return FALSE;
return TRUE;
}
static INT compare_substring( LPCWSTR a, INT operator, LPCWSTR b )
{
int lhs, rhs;
/* substring operators return 0 if LHS is missing */
if (!a || !*a)
return 0;
/* substring operators return 1 if RHS is missing */
if (!b || !*b)
return 1;
/* if both strings contain only numbers, use integer comparison */
lhs = atoiW(a);
rhs = atoiW(b);
if (str_is_number(a) && str_is_number(b))
return compare_int( lhs, operator, rhs );
switch (operator)
{
case COND_SS:
return strstrW( a, b ) ? 1 : 0;
case COND_ISS:
return strstriW( a, b ) ? 1 : 0;
case COND_LHS:
return 0 == strncmpW( a, b, lstrlenW( b ) );
case COND_RHS:
return 0 == lstrcmpW( a + (lstrlenW( a ) - lstrlenW( b )), b );
case COND_ILHS:
return 0 == strncmpiW( a, b, lstrlenW( b ) );
case COND_IRHS:
return 0 == lstrcmpiW( a + (lstrlenW( a ) - lstrlenW( b )), b );
default:
ERR("invalid substring operator\n");
return 0;
}
return 0;
}
static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b )
{
if (operator >= COND_SS && operator <= COND_RHS)
return compare_substring( a, operator, b );
/* null and empty string are equivalent */
if (!a) a = szEmpty;
if (!b) b = szEmpty;
/* a or b may be NULL */
switch (operator)
{
case COND_LT:
return -1 == lstrcmpW( a, b );
case COND_GT:
return 1 == lstrcmpW( a, b );
case COND_EQ:
return 0 == lstrcmpW( a, b );
case COND_NE:
return 0 != lstrcmpW( a, b );
case COND_GE:
return -1 != lstrcmpW( a, b );
case COND_LE:
return 1 != lstrcmpW( a, b );
case COND_ILT:
return -1 == lstrcmpiW( a, b );
case COND_IGT:
return 1 == lstrcmpiW( a, b );
case COND_IEQ:
return 0 == lstrcmpiW( a, b );
case COND_INE:
return 0 != lstrcmpiW( a, b );
case COND_IGE:
return -1 != lstrcmpiW( a, b );
case COND_ILE:
return 1 != lstrcmpiW( a, b );
default:
ERR("invalid string operator\n");
return 0;
}
return 0;
}
static INT compare_int( INT a, INT operator, INT b )
{
switch (operator)
{
case COND_LT:
case COND_ILT:
return a < b;
case COND_GT:
case COND_IGT:
return a > b;
case COND_EQ:
case COND_IEQ:
return a == b;
case COND_NE:
case COND_INE:
return a != b;
case COND_GE:
case COND_IGE:
return a >= b;
case COND_LE:
case COND_ILE:
return a <= b;
case COND_SS:
case COND_ISS:
return ( a & b ) ? 1 : 0;
case COND_RHS:
return ( ( a & 0xffff ) == b ) ? 1 : 0;
case COND_LHS:
return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0;
default:
ERR("invalid integer operator\n");
return 0;
}
return 0;
}
static int COND_IsIdent( WCHAR x )
{
return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' )
|| ( x == '#' ) || (x == '.') );
}
static int COND_GetOperator( COND_input *cond )
{
static const struct {
const WCHAR str[4];
int id;
} table[] = {
{ {'~','=',0}, COND_IEQ },
{ {'~','<','=',0}, COND_ILE },
{ {'~','>','<',0}, COND_ISS },
{ {'~','>','>',0}, COND_IRHS },
{ {'~','<','>',0}, COND_INE },
{ {'~','<',0}, COND_ILT },
{ {'~','>','=',0}, COND_IGE },
{ {'~','<','<',0}, COND_ILHS },
{ {'~','>',0}, COND_IGT },
{ {'>','=',0}, COND_GE },
{ {'>','<',0}, COND_SS },
{ {'<','<',0}, COND_LHS },
{ {'<','>',0}, COND_NE },
{ {'<','=',0}, COND_LE },
{ {'>','>',0}, COND_RHS },
{ {'>',0}, COND_GT },
{ {'<',0}, COND_LT },
{ {0}, 0 }
};
LPCWSTR p = &cond->str[cond->n];
int i = 0, len;
while ( 1 )
{
len = lstrlenW( table[i].str );
if ( !len || 0 == strncmpW( table[i].str, p, len ) )
break;
i++;
}
cond->n += len;
return table[i].id;
}
static int COND_GetOne( struct cond_str *str, COND_input *cond )
{
int rc, len = 1;
WCHAR ch;
str->data = &cond->str[cond->n];
ch = str->data[0];
switch( ch )
{
case 0: return 0;
case '(': rc = COND_LPAR; break;
case ')': rc = COND_RPAR; break;
case '&': rc = COND_AMPER; break;
case '!': rc = COND_EXCLAM; break;
case '$': rc = COND_DOLLARS; break;
case '?': rc = COND_QUESTION; break;
case '%': rc = COND_PERCENT; break;
case ' ': rc = COND_SPACE; break;
case '=': rc = COND_EQ; break;
case '~':
case '<':
case '>':
rc = COND_GetOperator( cond );
if (!rc)
rc = COND_ERROR;
return rc;
default:
rc = 0;
}
if ( rc )
{
cond->n += len;
return rc;
}
if (ch == '"' )
{
LPCWSTR p = strchrW( str->data + 1, '"' );
if (!p)
return COND_ERROR;
len = p - str->data + 1;
rc = COND_LITER;
}
else if( COND_IsAlpha( ch ) )
{
static const WCHAR szNot[] = {'N','O','T',0};
static const WCHAR szAnd[] = {'A','N','D',0};
static const WCHAR szXor[] = {'X','O','R',0};
static const WCHAR szEqv[] = {'E','Q','V',0};
static const WCHAR szImp[] = {'I','M','P',0};
static const WCHAR szOr[] = {'O','R',0};
while( COND_IsIdent( str->data[len] ) )
len++;
rc = COND_IDENT;
if ( len == 3 )
{
if ( !strncmpiW( str->data, szNot, len ) )
rc = COND_NOT;
else if( !strncmpiW( str->data, szAnd, len ) )
rc = COND_AND;
else if( !strncmpiW( str->data, szXor, len ) )
rc = COND_XOR;
else if( !strncmpiW( str->data, szEqv, len ) )
rc = COND_EQV;
else if( !strncmpiW( str->data, szImp, len ) )
rc = COND_IMP;
}
else if( (len == 2) && !strncmpiW( str->data, szOr, len ) )
rc = COND_OR;
}
else if( COND_IsNumber( ch ) )
{
while( COND_IsNumber( str->data[len] ) )
len++;
rc = COND_NUMBER;
}
else
{
ERR("Got unknown character %c(%x)\n",ch,ch);
return COND_ERROR;
}
cond->n += len;
str->len = len;
return rc;
}
static int cond_lex( void *COND_lval, COND_input *cond )
{
int rc;
struct cond_str *str = COND_lval;
do {
rc = COND_GetOne( str, cond );
} while (rc == COND_SPACE);
return rc;
}
static LPWSTR COND_GetString( const struct cond_str *str )
{
LPWSTR ret;
ret = msi_alloc( (str->len+1) * sizeof (WCHAR) );
if( ret )
{
memcpy( ret, str->data, str->len * sizeof(WCHAR));
ret[str->len]=0;
}
TRACE("Got identifier %s\n",debugstr_w(ret));
return ret;
}
static LPWSTR COND_GetLiteral( const struct cond_str *str )
{
LPWSTR ret;
ret = msi_alloc( (str->len-1) * sizeof (WCHAR) );
if( ret )
{
memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
ret[str->len - 2]=0;
}
TRACE("Got literal %s\n",debugstr_w(ret));
return ret;
}
static int cond_error(const char *str)
{
TRACE("%s\n", str );
return 0;
}
MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
{
COND_input cond;
MSICONDITION r;
TRACE("%s\n", debugstr_w( szCondition ) );
if ( szCondition == NULL )
return MSICONDITION_NONE;
cond.package = package;
cond.str = szCondition;
cond.n = 0;
cond.result = MSICONDITION_ERROR;
if ( !cond_parse( &cond ) )
r = cond.result;
else
r = MSICONDITION_ERROR;
TRACE("%i <- %s\n", r, debugstr_w(szCondition));
return r;
}
MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
{
MSIPACKAGE *package;
UINT ret;
package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
if( !package )
{
HRESULT hr;
BSTR condition;
IWineMsiRemotePackage *remote_package;
remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
if (!remote_package)
return MSICONDITION_ERROR;
condition = SysAllocString( szCondition );
if (!condition)
{
IWineMsiRemotePackage_Release( remote_package );
return ERROR_OUTOFMEMORY;
}
hr = IWineMsiRemotePackage_EvaluateCondition( remote_package, condition );
SysFreeString( condition );
IWineMsiRemotePackage_Release( remote_package );
if (FAILED(hr))
{
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
return HRESULT_CODE(hr);
return ERROR_FUNCTION_FAILED;
}
return ERROR_SUCCESS;
}
ret = MSI_EvaluateConditionW( package, szCondition );
msiobj_release( &package->hdr );
return ret;
}
MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
{
LPWSTR szwCond = NULL;
MSICONDITION r;
szwCond = strdupAtoW( szCondition );
if( szCondition && !szwCond )
return MSICONDITION_ERROR;
r = MsiEvaluateConditionW( hInstall, szwCond );
msi_free( szwCond );
return r;
}
--- NEW FILE: create.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSICREATEVIEW
{
MSIVIEW view;
MSIDATABASE *db;
LPWSTR name;
BOOL bIsTemp;
column_info *col_info;
} MSICREATEVIEW;
static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p %d %d %p\n", cv, row, col, val );
return ERROR_FUNCTION_FAILED;
}
static UINT CREATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
MSITABLE *table;
TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name),
cv->bIsTemp?"temporary":"permanent");
return msi_create_table( cv->db, cv->name, cv->col_info, !cv->bIsTemp, &table);
}
static UINT CREATE_close( struct tagMSIVIEW *view )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p\n", cv);
return ERROR_SUCCESS;
}
static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p %p %p\n", cv, rows, cols );
return ERROR_FUNCTION_FAILED;
}
static UINT CREATE_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p %d %p %p\n", cv, n, name, type );
return ERROR_FUNCTION_FAILED;
}
static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec)
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p %d %p\n", cv, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT CREATE_delete( struct tagMSIVIEW *view )
{
MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
TRACE("%p\n", cv );
msiobj_release( &cv->db->hdr );
msi_free( cv );
return ERROR_SUCCESS;
}
static const MSIVIEWOPS create_ops =
{
CREATE_fetch_int,
NULL,
NULL,
NULL,
NULL,
CREATE_execute,
CREATE_close,
CREATE_get_dimensions,
CREATE_get_column_info,
CREATE_modify,
CREATE_delete,
NULL,
NULL,
NULL,
NULL,
};
static UINT check_columns( column_info *col_info )
{
column_info *c1, *c2;
/* check for two columns with the same name */
for( c1 = col_info; c1; c1 = c1->next )
for( c2 = c1->next; c2; c2 = c2->next )
if (!lstrcmpW(c1->column, c2->column))
return ERROR_BAD_QUERY_SYNTAX;
return ERROR_SUCCESS;
}
UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
column_info *col_info, BOOL hold )
{
MSICREATEVIEW *cv = NULL;
UINT r;
const column_info *col;
BOOL temp = TRUE;
TRACE("%p\n", cv );
r = check_columns( col_info );
if( r != ERROR_SUCCESS )
return r;
cv = msi_alloc_zero( sizeof *cv );
if( !cv )
return ERROR_FUNCTION_FAILED;
for( col = col_info; col; col = col->next )
if( !col->temporary )
{
temp = FALSE;
break;
}
/* fill the structure */
cv->view.ops = &create_ops;
msiobj_addref( &db->hdr );
cv->db = db;
cv->name = table;
cv->col_info = col_info;
cv->bIsTemp = temp;
*view = (MSIVIEW*) cv;
return ERROR_SUCCESS;
}
--- NEW FILE: custom.c ---
/*
* Custom Action processing for the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1451 lines suppressed...]
mcr_AddRef,
mcr_Release,
mcr_GetActionInfo,
};
HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
{
msi_custom_remote_impl* This;
This = msi_alloc( sizeof *This );
if (!This)
return E_OUTOFMEMORY;
This->lpVtbl = &msi_custom_remote_vtbl;
This->refs = 1;
*ppObj = This;
return S_OK;
}
--- NEW FILE: database.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "msipriv.h"
#include "objidl.h"
#include "objbase.h"
#include "initguid.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
/*
* .MSI file format
*
* An .msi file is a structured storage file.
* It contains a number of streams.
* A stream for each table in the database.
* Two streams for the string table in the database.
* Any binary data in a table is a reference to a stream.
*/
static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
{
MSIDATABASE *db = (MSIDATABASE *) arg;
msi_free(db->path);
free_cached_tables( db );
msi_free_transforms( db );
msi_destroy_stringtable( db->strings );
IStorage_Release( db->storage );
if (db->deletefile)
{
DeleteFileW( db->deletefile );
msi_free( db->deletefile );
}
}
UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
{
IStorage *stg = NULL;
HRESULT r;
MSIDATABASE *db = NULL;
UINT ret = ERROR_FUNCTION_FAILED;
LPCWSTR szMode, save_path;
STATSTG stat;
BOOL created = FALSE;
WCHAR path[MAX_PATH];
static const WCHAR backslash[] = {'\\',0};
TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
if( !pdb )
return ERROR_INVALID_PARAMETER;
save_path = szDBPath;
szMode = szPersist;
if( HIWORD( szPersist ) )
{
if (!CopyFileW( szDBPath, szPersist, FALSE ))
return ERROR_OPEN_FAILED;
szDBPath = szPersist;
szPersist = MSIDBOPEN_TRANSACT;
created = TRUE;
}
if( szPersist == MSIDBOPEN_READONLY )
{
r = StgOpenStorage( szDBPath, NULL,
STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
}
else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
{
/* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
* used here: */
r = StgCreateDocfile( szDBPath,
STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
if( r == ERROR_SUCCESS )
{
IStorage_SetClass( stg, &CLSID_MsiDatabase );
r = msi_init_string_table( stg );
}
created = TRUE;
}
else if( szPersist == MSIDBOPEN_TRANSACT )
{
/* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
* used here: */
r = StgOpenStorage( szDBPath, NULL,
STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
}
else if( szPersist == MSIDBOPEN_DIRECT )
{
r = StgOpenStorage( szDBPath, NULL,
STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
}
else
{
ERR("unknown flag %p\n",szPersist);
return ERROR_INVALID_PARAMETER;
}
if( FAILED( r ) || !stg )
{
FIXME("open failed r = %08x!\n",r);
return ERROR_FUNCTION_FAILED;
}
r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
if( FAILED( r ) )
{
FIXME("Failed to stat storage\n");
goto end;
}
if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
!IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
{
ERR("storage GUID is not a MSI database GUID %s\n",
debugstr_guid(&stat.clsid) );
goto end;
}
db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
MSI_CloseDatabase );
if( !db )
{
FIXME("Failed to allocate a handle\n");
goto end;
}
if (!strchrW( save_path, '\\' ))
{
GetCurrentDirectoryW( MAX_PATH, path );
lstrcatW( path, backslash );
lstrcatW( path, save_path );
}
else
lstrcpyW( path, save_path );
db->path = strdupW( path );
if( TRACE_ON( msi ) )
enum_stream_names( stg );
db->storage = stg;
db->mode = szMode;
if (created)
db->deletefile = strdupW( szDBPath );
else
db->deletefile = NULL;
list_init( &db->tables );
list_init( &db->transforms );
db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
if( !db->strings )
goto end;
msi_table_set_strref( db->bytes_per_strref );
ret = ERROR_SUCCESS;
msiobj_addref( &db->hdr );
IStorage_AddRef( stg );
*pdb = db;
end:
if( db )
msiobj_release( &db->hdr );
if( stg )
IStorage_Release( stg );
return ret;
}
UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
{
MSIDATABASE *db;
UINT ret;
TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
if( ret == ERROR_SUCCESS )
{
*phDB = alloc_msihandle( &db->hdr );
if (! *phDB)
ret = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &db->hdr );
}
return ret;
}
UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
{
HRESULT r = ERROR_FUNCTION_FAILED;
LPWSTR szwDBPath = NULL, szwPersist = NULL;
TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
if( szDBPath )
{
szwDBPath = strdupAtoW( szDBPath );
if( !szwDBPath )
goto end;
}
if( HIWORD(szPersist) )
{
szwPersist = strdupAtoW( szPersist );
if( !szwPersist )
goto end;
}
else
szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
end:
if( HIWORD(szPersist) )
msi_free( szwPersist );
msi_free( szwDBPath );
return r;
}
static LPWSTR msi_read_text_archive(LPCWSTR path)
{
HANDLE file;
LPSTR data = NULL;
LPWSTR wdata = NULL;
DWORD read, size = 0;
file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
if (file == INVALID_HANDLE_VALUE)
return NULL;
size = GetFileSize( file, NULL );
data = msi_alloc( size + 1 );
if (!data)
goto done;
if (!ReadFile( file, data, size, &read, NULL ))
goto done;
data[size] = '\0';
wdata = strdupAtoW( data );
done:
CloseHandle( file );
msi_free( data );
return wdata;
}
static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
{
LPWSTR ptr = *line, save;
DWORD i, count = 1;
*entries = NULL;
/* stay on this line */
while (*ptr && *ptr != '\n')
{
/* entries are separated by tabs */
if (*ptr == '\t')
count++;
ptr++;
}
*entries = msi_alloc(count * sizeof(LPWSTR));
if (!*entries)
return;
/* store pointers into the data */
for (i = 0, ptr = *line; i < count; i++)
{
save = ptr;
while (*ptr && *ptr != '\t' && *ptr != '\n') ptr++;
/* NULL-separate the data */
if (*ptr)
*ptr++ = '\0';
(*entries)[i] = save;
}
/* move to the next line if there's more, else EOF */
*line = ptr;
if (num_entries)
*num_entries = count;
}
static LPWSTR msi_build_createsql_prelude(LPWSTR table)
{
LPWSTR prelude;
DWORD size;
static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
size = sizeof(create_fmt) + lstrlenW(table) - 2;
prelude = msi_alloc(size * sizeof(WCHAR));
if (!prelude)
return NULL;
sprintfW(prelude, create_fmt, table);
return prelude;
}
static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
{
LPWSTR columns;
LPCWSTR type;
DWORD sql_size = 1, i, len;
WCHAR expanded[128], *ptr;
WCHAR size[10], comma[2], extra[30];
static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
static const WCHAR size_fmt[] = {'(','%','s',')',0};
static const WCHAR type_char[] = {'C','H','A','R',0};
static const WCHAR type_int[] = {'I','N','T',0};
static const WCHAR type_long[] = {'L','O','N','G',0};
static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
for (i = 0; i < num_columns; i++)
{
type = NULL;
comma[1] = size[0] = extra[0] = '\0';
if (i == num_columns - 1)
comma[0] = '\0';
else
comma[0] = ',';
ptr = &types[i][1];
len = atolW(ptr);
extra[0] = '\0';
switch (types[i][0])
{
case 'l':
lstrcpyW(extra, type_notnull);
case 'L':
lstrcatW(extra, localizable);
type = type_char;
sprintfW(size, size_fmt, ptr);
break;
case 's':
lstrcpyW(extra, type_notnull);
case 'S':
type = type_char;
sprintfW(size, size_fmt, ptr);
break;
case 'i':
lstrcpyW(extra, type_notnull);
case 'I':
if (len == 2)
type = type_int;
else
type = type_long;
break;
}
sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
sql_size += lstrlenW(expanded);
columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
lstrcatW(columns, expanded);
}
return columns;
}
static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
{
LPWSTR postlude, keys, ptr;
DWORD size, key_size, i;
static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
for (i = 0, size = 1; i < num_keys; i++)
size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
keys = msi_alloc(size * sizeof(WCHAR));
if (!keys)
return NULL;
for (i = 0, ptr = keys; i < num_keys; i++)
{
key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
sprintfW(ptr, key_fmt, primary_keys[i]);
ptr += key_size;
}
/* remove final ', ' */
*(ptr - 2) = '\0';
size = lstrlenW(postlude_fmt) + size - 1;
postlude = msi_alloc(size * sizeof(WCHAR));
if (!postlude)
goto done;
sprintfW(postlude, postlude_fmt, keys);
done:
msi_free(keys);
return postlude;
}
static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
{
UINT r;
DWORD size;
MSIQUERY *view;
LPWSTR create_sql;
LPWSTR prelude, columns_sql, postlude;
prelude = msi_build_createsql_prelude(labels[0]);
columns_sql = msi_build_createsql_columns(columns, types, num_columns);
postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
if (!prelude || !columns_sql || !postlude)
return ERROR_OUTOFMEMORY;
size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
create_sql = msi_alloc(size * sizeof(WCHAR));
if (!create_sql)
return ERROR_OUTOFMEMORY;
lstrcpyW(create_sql, prelude);
lstrcatW(create_sql, columns_sql);
lstrcatW(create_sql, postlude);
msi_free(prelude);
msi_free(columns_sql);
msi_free(postlude);
r = MSI_DatabaseOpenViewW( db, create_sql, &view );
msi_free(create_sql);
if (r != ERROR_SUCCESS)
return r;
r = MSI_ViewExecute(view, NULL);
MSI_ViewClose(view);
msiobj_release(&view->hdr);
return r;
}
static LPWSTR msi_build_insertsql_prelude(LPWSTR table)
{
LPWSTR prelude;
DWORD size;
static const WCHAR insert_fmt[] = {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','%','s','`',' ','(',' ',0};
size = sizeof(insert_fmt) + lstrlenW(table) - 2;
prelude = msi_alloc(size * sizeof(WCHAR));
if (!prelude)
return NULL;
sprintfW(prelude, insert_fmt, table);
return prelude;
}
static LPWSTR msi_build_insertsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
{
LPWSTR columns;
DWORD sql_size = 1, i;
WCHAR expanded[128];
static const WCHAR column_fmt[] = {'`','%','s','`',',',' ',0};
columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
for (i = 0; i < num_columns; i++)
{
sprintfW(expanded, column_fmt, columns_data[i]);
sql_size += lstrlenW(expanded);
if (i == num_columns - 1)
{
sql_size -= 2;
expanded[lstrlenW(expanded) - 2] = '\0';
}
columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
lstrcatW(columns, expanded);
}
return columns;
}
static LPWSTR msi_build_insertsql_data(LPWSTR **records, LPWSTR *types, DWORD num_columns, DWORD irec)
{
LPWSTR columns;
DWORD sql_size = 1, i;
WCHAR expanded[128];
static const WCHAR str_fmt[] = {'\'','%','s','\'',',',' ',0};
static const WCHAR int_fmt[] = {'%','s',',',' ',0};
static const WCHAR empty[] = {'\'','\'',',',' ',0};
columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
for (i = 0; i < num_columns; i++)
{
switch (types[i][0])
{
case 'L': case 'l': case 'S': case 's':
sprintfW(expanded, str_fmt, records[irec][i]);
break;
case 'I': case 'i':
if (*records[0][i])
sprintfW(expanded, int_fmt, records[irec][i]);
else
lstrcpyW(expanded, empty);
break;
default:
return NULL;
}
if (i == num_columns - 1)
expanded[lstrlenW(expanded) - 2] = '\0';
sql_size += lstrlenW(expanded);
columns = msi_realloc(columns, sql_size * sizeof(WCHAR));
if (!columns)
return NULL;
lstrcatW(columns, expanded);
}
return columns;
}
static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
LPWSTR *labels, LPWSTR **records,
int num_columns, int num_records)
{
MSIQUERY *view;
LPWSTR insert_sql;
DWORD size, i;
UINT r = ERROR_SUCCESS;
static const WCHAR mid[] = {' ',')',' ','V','A','L','U','E','S',' ','(',' ',0};
static const WCHAR end[] = {' ',')',0};
LPWSTR prelude = msi_build_insertsql_prelude(labels[0]);
LPWSTR columns_sql = msi_build_insertsql_columns(columns, types, num_columns);
for (i = 0; i < num_records; i++)
{
LPWSTR data = msi_build_insertsql_data(records, types, num_columns, i);
size = lstrlenW(prelude) + lstrlenW(columns_sql) + sizeof(mid) + lstrlenW(data) + sizeof(end) - 1;
insert_sql = msi_alloc(size * sizeof(WCHAR));
if (!insert_sql)
return ERROR_OUTOFMEMORY;
lstrcpyW(insert_sql, prelude);
lstrcatW(insert_sql, columns_sql);
lstrcatW(insert_sql, mid);
lstrcatW(insert_sql, data);
lstrcatW(insert_sql, end);
msi_free(data);
r = MSI_DatabaseOpenViewW( db, insert_sql, &view );
msi_free(insert_sql);
if (r != ERROR_SUCCESS)
goto done;
r = MSI_ViewExecute(view, NULL);
MSI_ViewClose(view);
msiobj_release(&view->hdr);
}
done:
msi_free(prelude);
msi_free(columns_sql);
return r;
}
UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
{
UINT r;
DWORD len, i;
DWORD num_labels;
DWORD num_columns, num_records = 0;
LPWSTR *columns, *types, *labels;
LPWSTR path, ptr, data;
LPWSTR **records;
static const WCHAR backslash[] = {'\\',0};
TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
if( folder == NULL || file == NULL )
return ERROR_INVALID_PARAMETER;
len = lstrlenW(folder) + lstrlenW(backslash) + lstrlenW(file) + 1;
path = msi_alloc( len * sizeof(WCHAR) );
if (!path)
return ERROR_OUTOFMEMORY;
lstrcpyW( path, folder );
lstrcatW( path, backslash );
lstrcatW( path, file );
data = msi_read_text_archive( path );
ptr = data;
msi_parse_line( &ptr, &columns, &num_columns );
msi_parse_line( &ptr, &types, NULL );
msi_parse_line( &ptr, &labels, &num_labels );
records = msi_alloc(sizeof(LPWSTR *));
if (!records)
return ERROR_OUTOFMEMORY;
/* read in the table records */
while (*ptr)
{
msi_parse_line( &ptr, &records[num_records], NULL );
num_records++;
records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
if (!records)
return ERROR_OUTOFMEMORY;
}
r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
if (r != ERROR_SUCCESS)
goto done;
r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records );
done:
msi_free(path);
msi_free(data);
msi_free(columns);
msi_free(types);
msi_free(labels);
for (i = 0; i < num_records; i++)
msi_free(records[i]);
msi_free(records);
return r;
}
UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
{
MSIDATABASE *db;
UINT r;
TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
r = MSI_DatabaseImport( db, szFolder, szFilename );
msiobj_release( &db->hdr );
return r;
}
UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
LPCSTR szFolder, LPCSTR szFilename )
{
LPWSTR path = NULL, file = NULL;
UINT r = ERROR_OUTOFMEMORY;
TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
if( szFolder )
{
path = strdupAtoW( szFolder );
if( !path )
goto end;
}
if( szFilename )
{
file = strdupAtoW( szFilename );
if( !file )
goto end;
}
r = MsiDatabaseImportW( handle, path, file );
end:
msi_free( path );
msi_free( file );
return r;
}
static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
{
UINT i, count, len, r = ERROR_SUCCESS;
const char *sep;
char *buffer;
DWORD sz;
len = 0x100;
buffer = msi_alloc( len );
if ( !buffer )
return ERROR_OUTOFMEMORY;
count = MSI_RecordGetFieldCount( row );
for ( i=start; i<=count; i++ )
{
sz = len;
r = MSI_RecordGetStringA( row, i, buffer, &sz );
if (r == ERROR_MORE_DATA)
{
char *p = msi_realloc( buffer, sz + 1 );
if (!p)
break;
len = sz + 1;
buffer = p;
}
sz = len;
r = MSI_RecordGetStringA( row, i, buffer, &sz );
if (r != ERROR_SUCCESS)
break;
if (!WriteFile( handle, buffer, sz, &sz, NULL ))
{
r = ERROR_FUNCTION_FAILED;
break;
}
sep = (i < count) ? "\t" : "\r\n";
if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
{
r = ERROR_FUNCTION_FAILED;
break;
}
}
msi_free( buffer );
return r;
}
static UINT msi_export_row( MSIRECORD *row, void *arg )
{
return msi_export_record( arg, row, 1 );
}
UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
LPCWSTR folder, LPCWSTR file )
{
static const WCHAR query[] = {
's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
static const WCHAR szbs[] = { '\\', 0 };
MSIRECORD *rec = NULL;
MSIQUERY *view = NULL;
LPWSTR filename;
HANDLE handle;
UINT len, r;
TRACE("%p %s %s %s\n", db, debugstr_w(table),
debugstr_w(folder), debugstr_w(file) );
if( folder == NULL || file == NULL )
return ERROR_INVALID_PARAMETER;
len = lstrlenW(folder) + lstrlenW(file) + 2;
filename = msi_alloc(len * sizeof (WCHAR));
if (!filename)
return ERROR_OUTOFMEMORY;
lstrcpyW( filename, folder );
lstrcatW( filename, szbs );
lstrcatW( filename, file );
handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
msi_free( filename );
if (handle == INVALID_HANDLE_VALUE)
return ERROR_FUNCTION_FAILED;
r = MSI_OpenQuery( db, &view, query, table );
if (r == ERROR_SUCCESS)
{
/* write out row 1, the column names */
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
if (r == ERROR_SUCCESS)
{
msi_export_record( handle, rec, 1 );
msiobj_release( &rec->hdr );
}
/* write out row 2, the column types */
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
if (r == ERROR_SUCCESS)
{
msi_export_record( handle, rec, 1 );
msiobj_release( &rec->hdr );
}
/* write out row 3, the table name + keys */
r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
if (r == ERROR_SUCCESS)
{
MSI_RecordSetStringW( rec, 0, table );
msi_export_record( handle, rec, 0 );
msiobj_release( &rec->hdr );
}
/* write out row 4 onwards, the data */
r = MSI_IterateRecords( view, 0, msi_export_row, handle );
msiobj_release( &view->hdr );
}
CloseHandle( handle );
return r;
}
/***********************************************************************
* MsiExportDatabaseW [MSI.@]
*
* Writes a file containing the table data as tab separated ASCII.
*
* The format is as follows:
*
* row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
* row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
* row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
*
* Followed by the data, starting at row 1 with one row per line
*
* row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
*/
UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
LPCWSTR szFolder, LPCWSTR szFilename )
{
MSIDATABASE *db;
UINT r;
TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
debugstr_w(szFolder), debugstr_w(szFilename));
db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
msiobj_release( &db->hdr );
return r;
}
UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
LPCSTR szFolder, LPCSTR szFilename )
{
LPWSTR path = NULL, file = NULL, table = NULL;
UINT r = ERROR_OUTOFMEMORY;
TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
debugstr_a(szFolder), debugstr_a(szFilename));
if( szTable )
{
table = strdupAtoW( szTable );
if( !table )
goto end;
}
if( szFolder )
{
path = strdupAtoW( szFolder );
if( !path )
goto end;
}
if( szFilename )
{
file = strdupAtoW( szFilename );
if( !file )
goto end;
}
r = MsiDatabaseExportW( handle, table, path, file );
end:
msi_free( table );
msi_free( path );
msi_free( file );
return r;
}
MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
{
MSIDBSTATE ret = MSIDBSTATE_READ;
MSIDATABASE *db;
TRACE("%ld\n", handle);
db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
if (!db)
return MSIDBSTATE_ERROR;
if (db->mode != MSIDBOPEN_READONLY )
ret = MSIDBSTATE_WRITE;
msiobj_release( &db->hdr );
return ret;
}
--- NEW FILE: delete.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/*
* Code to delete rows from a table.
*
* We delete rows by blanking them out rather than trying to remove the row.
* This appears to be what the native MSI does (or tries to do). For the query:
*
* delete from Property
*
* some non-zero entries are left in the table by native MSI. I'm not sure if
* that's a bug in the way I'm running the query, or a just a bug.
*/
typedef struct tagMSIDELETEVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
} MSIDELETEVIEW;
static UINT DELETE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p %d %d %p\n", dv, row, col, val );
return ERROR_FUNCTION_FAILED;
}
static UINT DELETE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p %d %d %p\n", dv, row, col, stm );
return ERROR_FUNCTION_FAILED;
}
static UINT DELETE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
UINT r, i, rows = 0, cols = 0;
MSIRECORD *empty;
TRACE("%p %p\n", dv, record);
if( !dv->table )
return ERROR_FUNCTION_FAILED;
r = dv->table->ops->execute( dv->table, record );
if( r != ERROR_SUCCESS )
return r;
r = dv->table->ops->get_dimensions( dv->table, &rows, &cols );
if( r != ERROR_SUCCESS )
return r;
TRACE("blanking %d rows\n", rows);
empty = MSI_CreateRecord( cols );
if (!empty)
return ERROR_FUNCTION_FAILED;
/* blank out all the rows that match */
for ( i=0; i<rows; i++ )
dv->table->ops->set_row( dv->table, i, empty, (1<<cols)-1 );
msiobj_release( &empty->hdr );
return ERROR_SUCCESS;
}
static UINT DELETE_close( struct tagMSIVIEW *view )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p\n", dv );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
return dv->table->ops->close( dv->table );
}
static UINT DELETE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p %p %p\n", dv, rows, cols );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
*rows = 0;
return dv->table->ops->get_dimensions( dv->table, NULL, cols );
}
static UINT DELETE_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p %d %p %p\n", dv, n, name, type );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
return dv->table->ops->get_column_info( dv->table, n, name, type );
}
static UINT DELETE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p %d %p\n", dv, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT DELETE_delete( struct tagMSIVIEW *view )
{
MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
TRACE("%p\n", dv );
if( dv->table )
dv->table->ops->delete( dv->table );
msi_free( dv );
return ERROR_SUCCESS;
}
static UINT DELETE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
return ERROR_FUNCTION_FAILED;
}
static const MSIVIEWOPS delete_ops =
{
DELETE_fetch_int,
DELETE_fetch_stream,
NULL,
NULL,
NULL,
DELETE_execute,
DELETE_close,
DELETE_get_dimensions,
DELETE_get_column_info,
DELETE_modify,
DELETE_delete,
DELETE_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
{
MSIDELETEVIEW *dv = NULL;
TRACE("%p\n", dv );
dv = msi_alloc_zero( sizeof *dv );
if( !dv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
dv->view.ops = &delete_ops;
dv->db = db;
dv->table = table;
*view = &dv->view;
return ERROR_SUCCESS;
}
--- NEW FILE: dialog.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...3589 lines suppressed...]
if ( !dialog )
return ERROR_FUNCTION_FAILED;
dialog->finished = FALSE;
r = msi_dialog_run_message_loop( dialog );
if ( r != ERROR_SUCCESS )
goto done;
r = MSI_GetPropertyW( package, result_prop, result, &size );
if ( r != ERROR_SUCCESS)
r = ERROR_SUCCESS;
if ( !lstrcmpW( result, error_abort ) )
r = ERROR_FUNCTION_FAILED;
done:
msi_dialog_destroy( dialog );
return r;
}
--- NEW FILE: distinct.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
typedef struct tagDISTINCTSET
{
UINT val;
UINT count;
UINT row;
struct tagDISTINCTSET *nextrow;
struct tagDISTINCTSET *nextcol;
} DISTINCTSET;
typedef struct tagMSIDISTINCTVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
UINT row_count;
UINT *translation;
} MSIDISTINCTVIEW;
static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row )
{
/* horrible O(n) find */
while( *x )
{
if( (*x)->val == val )
{
(*x)->count++;
return x;
}
x = &(*x)->nextrow;
}
/* nothing found, so add one */
*x = msi_alloc( sizeof (DISTINCTSET) );
if( *x )
{
(*x)->val = val;
(*x)->count = 1;
(*x)->row = row;
(*x)->nextrow = NULL;
(*x)->nextcol = NULL;
}
return x;
}
static void distinct_free( DISTINCTSET *x )
{
while( x )
{
DISTINCTSET *next = x->nextrow;
distinct_free( x->nextcol );
msi_free( x );
x = next;
}
}
static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p %d %d %p\n", dv, row, col, val );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
if( row >= dv->row_count )
return ERROR_INVALID_PARAMETER;
row = dv->translation[ row ];
return dv->table->ops->fetch_int( dv->table, row, col, val );
}
static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
UINT r, i, j, r_count, c_count;
DISTINCTSET *rowset = NULL;
TRACE("%p %p\n", dv, record);
if( !dv->table )
return ERROR_FUNCTION_FAILED;
r = dv->table->ops->execute( dv->table, record );
if( r != ERROR_SUCCESS )
return r;
r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count );
if( r != ERROR_SUCCESS )
return r;
dv->translation = msi_alloc( r_count*sizeof(UINT) );
if( !dv->translation )
return ERROR_FUNCTION_FAILED;
/* build it */
for( i=0; i<r_count; i++ )
{
DISTINCTSET **x = &rowset;
for( j=1; j<=c_count; j++ )
{
UINT val = 0;
r = dv->table->ops->fetch_int( dv->table, i, j, &val );
if( r != ERROR_SUCCESS )
{
ERR("Failed to fetch int at %d %d\n", i, j );
distinct_free( rowset );
return r;
}
x = distinct_insert( x, val, i );
if( !*x )
{
ERR("Failed to insert at %d %d\n", i, j );
distinct_free( rowset );
return ERROR_FUNCTION_FAILED;
}
if( j != c_count )
x = &(*x)->nextcol;
}
/* check if it was distinct and if so, include it */
if( (*x)->row == i )
{
TRACE("Row %d -> %d\n", dv->row_count, i);
dv->translation[dv->row_count++] = i;
}
}
distinct_free( rowset );
return ERROR_SUCCESS;
}
static UINT DISTINCT_close( struct tagMSIVIEW *view )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p\n", dv );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
msi_free( dv->translation );
dv->translation = NULL;
dv->row_count = 0;
return dv->table->ops->close( dv->table );
}
static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p %p %p\n", dv, rows, cols );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
if( rows )
{
if( !dv->translation )
return ERROR_FUNCTION_FAILED;
*rows = dv->row_count;
}
return dv->table->ops->get_dimensions( dv->table, NULL, cols );
}
static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p %d %p %p\n", dv, n, name, type );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
return dv->table->ops->get_column_info( dv->table, n, name, type );
}
static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p %d %p\n", dv, eModifyMode, rec );
if( !dv->table )
return ERROR_FUNCTION_FAILED;
return dv->table->ops->modify( dv->table, eModifyMode, rec );
}
static UINT DISTINCT_delete( struct tagMSIVIEW *view )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
TRACE("%p\n", dv );
if( dv->table )
dv->table->ops->delete( dv->table );
msi_free( dv->translation );
msiobj_release( &dv->db->hdr );
msi_free( dv );
return ERROR_SUCCESS;
}
static UINT DISTINCT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
UINT r;
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
if( !dv->table )
return ERROR_FUNCTION_FAILED;
r = dv->table->ops->find_matching_rows( dv->table, col, val, row, handle );
if( *row > dv->row_count )
return ERROR_NO_MORE_ITEMS;
*row = dv->translation[ *row ];
return r;
}
static const MSIVIEWOPS distinct_ops =
{
DISTINCT_fetch_int,
NULL,
NULL,
NULL,
NULL,
DISTINCT_execute,
DISTINCT_close,
DISTINCT_get_dimensions,
DISTINCT_get_column_info,
DISTINCT_modify,
DISTINCT_delete,
DISTINCT_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
{
MSIDISTINCTVIEW *dv = NULL;
UINT count = 0, r;
TRACE("%p\n", dv );
r = table->ops->get_dimensions( table, NULL, &count );
if( r != ERROR_SUCCESS )
{
ERR("can't get table dimensions\n");
return r;
}
dv = msi_alloc_zero( sizeof *dv );
if( !dv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
dv->view.ops = &distinct_ops;
msiobj_addref( &db->hdr );
dv->db = db;
dv->table = table;
dv->translation = NULL;
dv->row_count = 0;
*view = (MSIVIEW*) dv;
return ERROR_SUCCESS;
}
--- NEW FILE: events.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/controlevent_overview.asp
*/
#include <stdarg.h>
#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "wine-lgpl/msi.h"
#include "msipriv.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
typedef UINT (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *);
struct _events {
LPCSTR event;
EVENTHANDLER handler;
};
struct subscriber {
struct list entry;
msi_dialog *dialog;
LPWSTR event;
LPWSTR control;
LPWSTR attribute;
};
static UINT ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*);
/*
* Create a dialog box and run it if it's modal
*/
static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name, msi_dialog *parent, BOOL destroy_modeless )
{
msi_dialog *dialog;
UINT r;
/* create a new dialog */
dialog = msi_dialog_create( package, name, parent,
ControlEvent_HandleControlEvent );
if( dialog )
{
/* kill the current modeless dialog */
if( destroy_modeless && package->dialog )
{
msi_dialog_destroy( package->dialog );
package->dialog = NULL;
}
/* modeless dialogs return an error message */
r = msi_dialog_run_message_loop( dialog );
if( r == ERROR_SUCCESS )
msi_dialog_destroy( dialog );
else
package->dialog = dialog;
}
else
r = ERROR_FUNCTION_FAILED;
return r;
}
/*
* End a modal dialog box
*/
static UINT ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
static const WCHAR szExit[] = {
'E','x','i','t',0};
static const WCHAR szRetry[] = {
'R','e','t','r','y',0};
static const WCHAR szIgnore[] = {
'I','g','n','o','r','e',0};
static const WCHAR szReturn[] = {
'R','e','t','u','r','n',0};
if (lstrcmpW(argument,szExit)==0)
package->CurrentInstallState = ERROR_INSTALL_USEREXIT;
else if (lstrcmpW(argument, szRetry) == 0)
package->CurrentInstallState = ERROR_INSTALL_SUSPEND;
else if (lstrcmpW(argument, szIgnore) == 0)
package->CurrentInstallState = -1;
else if (lstrcmpW(argument, szReturn) == 0)
{
msi_dialog *parent = msi_dialog_get_parent(dialog);
msi_free(package->next_dialog);
package->next_dialog = (parent) ? strdupW(msi_dialog_get_name(parent)) : NULL;
package->CurrentInstallState = ERROR_SUCCESS;
}
else
{
ERR("Unknown argument string %s\n",debugstr_w(argument));
package->CurrentInstallState = ERROR_FUNCTION_FAILED;
}
ControlEvent_CleanupDialogSubscriptions(package, msi_dialog_get_name( dialog ));
msi_dialog_end_dialog( dialog );
return ERROR_SUCCESS;
}
/*
* transition from one modal dialog to another modal dialog
*/
static UINT ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog *dialog)
{
/* store the name of the next dialog, and signal this one to end */
package->next_dialog = strdupW(argument);
ControlEvent_CleanupSubscriptions(package);
msi_dialog_end_dialog( dialog );
return ERROR_SUCCESS;
}
/*
* Create a new child dialog of an existing modal dialog
*/
static UINT ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog *dialog)
{
/* don't destroy a modeless dialogs that might be our parent */
event_do_dialog( package, argument, dialog, FALSE );
if( package->CurrentInstallState != ERROR_SUCCESS )
msi_dialog_end_dialog( dialog );
return ERROR_SUCCESS;
}
/*
* Creates a dialog that remains up for a period of time
* based on a condition
*/
static UINT ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
FIXME("Doing Nothing\n");
return ERROR_SUCCESS;
}
static UINT ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
ACTION_PerformAction(package,argument,-1,TRUE);
return ERROR_SUCCESS;
}
static UINT ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
static const WCHAR szAll[] = {'A','L','L',0};
MSIFEATURE *feature = NULL;
if (lstrcmpW(szAll,argument))
{
MSI_SetFeatureStateW(package,argument,INSTALLSTATE_LOCAL);
}
else
{
LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
msi_feature_set_state( feature, INSTALLSTATE_LOCAL );
ACTION_UpdateComponentStates(package,argument);
}
return ERROR_SUCCESS;
}
static UINT ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
static const WCHAR szAll[] = {'A','L','L',0};
MSIFEATURE *feature = NULL;
if (lstrcmpW(szAll,argument))
{
MSI_SetFeatureStateW(package,argument,INSTALLSTATE_ABSENT);
}
else
{
LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
msi_feature_set_state( feature, INSTALLSTATE_ABSENT );
ACTION_UpdateComponentStates(package,argument);
}
return ERROR_SUCCESS;
}
static UINT ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
static const WCHAR szAll[] = {'A','L','L',0};
MSIFEATURE *feature = NULL;
if (lstrcmpW(szAll,argument))
{
MSI_SetFeatureStateW(package,argument,INSTALLSTATE_SOURCE);
}
else
{
LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
msi_feature_set_state( feature, INSTALLSTATE_SOURCE );
ACTION_UpdateComponentStates(package,argument);
}
return ERROR_SUCCESS;
}
static UINT ControlEvent_SetTargetPath(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
LPWSTR path = msi_dup_property( package, argument );
MSIRECORD *rec = MSI_CreateRecord( 1 );
UINT r;
static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0};
MSI_RecordSetStringW( rec, 1, path );
ControlEvent_FireSubscribedEvent( package, szSelectionPath, rec );
/* failure to set the path halts the executing of control events */
r = MSI_SetTargetPathW(package, argument, path);
msi_free(path);
msi_free(&rec->hdr);
return r;
}
static UINT ControlEvent_Reset(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
msi_dialog_reset(dialog);
return ERROR_SUCCESS;
}
/*
* Subscribed events
*/
static void free_subscriber( struct subscriber *sub )
{
msi_free(sub->event);
msi_free(sub->control);
msi_free(sub->attribute);
msi_free(sub);
}
VOID ControlEvent_SubscribeToEvent( MSIPACKAGE *package, msi_dialog *dialog,
LPCWSTR event, LPCWSTR control, LPCWSTR attribute )
{
struct subscriber *sub;
sub = msi_alloc(sizeof (*sub));
if( !sub )
return;
sub->dialog = dialog;
sub->event = strdupW(event);
sub->control = strdupW(control);
sub->attribute = strdupW(attribute);
list_add_tail( &package->subscriptions, &sub->entry );
}
VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event,
LPCWSTR control, LPCWSTR attribute )
{
struct list *i, *t;
struct subscriber *sub;
LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
{
sub = LIST_ENTRY( i, struct subscriber, entry );
if( lstrcmpiW(sub->control,control) )
continue;
if( lstrcmpiW(sub->attribute,attribute) )
continue;
if( lstrcmpiW(sub->event,event) )
continue;
list_remove( &sub->entry );
free_subscriber( sub );
}
}
VOID ControlEvent_FireSubscribedEvent( MSIPACKAGE *package, LPCWSTR event,
MSIRECORD *rec )
{
struct subscriber *sub;
TRACE("Firing Event %s\n",debugstr_w(event));
LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry )
{
if (lstrcmpiW(sub->event, event))
continue;
msi_dialog_handle_event( sub->dialog, sub->control,
sub->attribute, rec );
}
}
VOID ControlEvent_CleanupDialogSubscriptions(MSIPACKAGE *package, LPWSTR dialog)
{
struct list *i, *t;
struct subscriber *sub;
LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
{
sub = LIST_ENTRY( i, struct subscriber, entry );
if ( lstrcmpW( msi_dialog_get_name( sub->dialog ), dialog ))
continue;
list_remove( &sub->entry );
free_subscriber( sub );
}
}
VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package)
{
struct list *i, *t;
struct subscriber *sub;
LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
{
sub = LIST_ENTRY( i, struct subscriber, entry );
list_remove( &sub->entry );
free_subscriber( sub );
}
}
/*
* ACTION_DialogBox()
*
* Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
* if the given parameter is not a dialog box
*/
UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName )
{
UINT r = ERROR_SUCCESS;
if( package->next_dialog )
ERR("Already a next dialog... ignoring it\n");
package->next_dialog = NULL;
/*
* Dialogs are chained by filling in the next_dialog member
* of the package structure, then terminating the current dialog.
* The code below sees the next_dialog member set, and runs the
* next dialog.
* We fall out of the loop below if we come across a modeless
* dialog, as it returns ERROR_IO_PENDING when we try to run
* its message loop.
*/
r = event_do_dialog( package, szDialogName, NULL, TRUE );
while( r == ERROR_SUCCESS && package->next_dialog )
{
LPWSTR name = package->next_dialog;
package->next_dialog = NULL;
r = event_do_dialog( package, name, NULL, TRUE );
msi_free( name );
}
if( r == ERROR_IO_PENDING )
r = ERROR_SUCCESS;
return r;
}
static UINT ControlEvent_SetInstallLevel(MSIPACKAGE* package, LPCWSTR argument,
msi_dialog* dialog)
{
int iInstallLevel = atolW(argument);
TRACE("Setting install level: %i\n", iInstallLevel);
return MSI_SetInstallLevel( package, iInstallLevel );
}
static UINT ControlEvent_DirectoryListUp(MSIPACKAGE *package, LPCWSTR argument,
msi_dialog *dialog)
{
return msi_dialog_directorylist_up( dialog );
}
static UINT ControlEvent_ReinstallMode(MSIPACKAGE *package, LPCWSTR argument,
msi_dialog *dialog)
{
static const WCHAR szReinstallMode[] = {'R','E','I','N','S','T','A','L','L','M','O','D','E',0};
return MSI_SetPropertyW( package, szReinstallMode, argument );
}
static const struct _events Events[] = {
{ "EndDialog",ControlEvent_EndDialog },
{ "NewDialog",ControlEvent_NewDialog },
{ "SpawnDialog",ControlEvent_SpawnDialog },
{ "SpawnWaitDialog",ControlEvent_SpawnWaitDialog },
{ "DoAction",ControlEvent_DoAction },
{ "AddLocal",ControlEvent_AddLocal },
{ "Remove",ControlEvent_Remove },
{ "AddSource",ControlEvent_AddSource },
{ "SetTargetPath",ControlEvent_SetTargetPath },
{ "Reset",ControlEvent_Reset },
{ "SetInstallLevel",ControlEvent_SetInstallLevel },
{ "DirectoryListUp",ControlEvent_DirectoryListUp },
{ "SelectionBrowse",ControlEvent_SpawnDialog },
{ "ReinstallMode",ControlEvent_ReinstallMode },
{ NULL,NULL },
};
UINT ControlEvent_HandleControlEvent(MSIPACKAGE *package, LPCWSTR event,
LPCWSTR argument, msi_dialog* dialog)
{
int i = 0;
UINT rc = ERROR_SUCCESS;
TRACE("Handling Control Event %s\n",debugstr_w(event));
if (!event)
return rc;
while( Events[i].event != NULL)
{
LPWSTR wevent = strdupAtoW(Events[i].event);
if (lstrcmpW(wevent,event)==0)
{
msi_free(wevent);
rc = Events[i].handler(package,argument,dialog);
return rc;
}
msi_free(wevent);
i++;
}
FIXME("unhandled control event %s arg(%s)\n",
debugstr_w(event), debugstr_w(argument));
return rc;
}
--- NEW FILE: files.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...969 lines suppressed...]
if ( !DeleteFileW( file->TargetPath ) )
ERR("failed to delete %s\n", debugstr_w(file->TargetPath) );
file->state = msifs_missing;
/* the UI chunk */
uirow = MSI_CreateRecord( 9 );
MSI_RecordSetStringW( uirow, 1, file->FileName );
uipath = strdupW( file->TargetPath );
p = strrchrW(uipath,'\\');
if (p)
p[1]=0;
MSI_RecordSetStringW( uirow, 9, uipath);
ui_actiondata( package, szRemoveFiles, uirow);
msiobj_release( &uirow->hdr );
msi_free( uipath );
/* FIXME: call ui_progress here? */
}
return ERROR_SUCCESS;
}
--- NEW FILE: font.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2004,2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "wine/debug.h"
#include "msipriv.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
typedef struct _tagTT_OFFSET_TABLE {
USHORT uMajorVersion;
USHORT uMinorVersion;
USHORT uNumOfTables;
USHORT uSearchRange;
USHORT uEntrySelector;
USHORT uRangeShift;
} TT_OFFSET_TABLE;
typedef struct _tagTT_TABLE_DIRECTORY {
char szTag[4]; /* table name */
ULONG uCheckSum; /* Check sum */
ULONG uOffset; /* Offset from beginning of file */
ULONG uLength; /* length of the table in bytes */
} TT_TABLE_DIRECTORY;
typedef struct _tagTT_NAME_TABLE_HEADER {
USHORT uFSelector; /* format selector. Always 0 */
USHORT uNRCount; /* Name Records count */
USHORT uStorageOffset; /* Offset for strings storage,
* from start of the table */
} TT_NAME_TABLE_HEADER;
typedef struct _tagTT_NAME_RECORD {
USHORT uPlatformID;
USHORT uEncodingID;
USHORT uLanguageID;
USHORT uNameID;
USHORT uStringLength;
USHORT uStringOffset; /* from start of storage area */
} TT_NAME_RECORD;
#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
static const WCHAR szRegisterFonts[] =
{'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
/*
* Code based off of code located here
* http://www.codeproject.com/gdi/fontnamefromfile.asp
*
* Using string index 4 (full font name) instead of 1 (family name)
*/
static LPWSTR load_ttfname_from(LPCWSTR filename)
{
TT_TABLE_DIRECTORY tblDir;
BOOL bFound = FALSE;
TT_OFFSET_TABLE ttOffsetTable;
TT_NAME_TABLE_HEADER ttNTHeader;
TT_NAME_RECORD ttRecord;
DWORD dwRead;
HANDLE handle;
LPWSTR ret = NULL;
int i;
handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0 );
if (handle == INVALID_HANDLE_VALUE)
{
ERR("Unable to open font file %s\n", debugstr_w(filename));
return NULL;
}
if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
goto end;
ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
goto end;
for (i=0; i< ttOffsetTable.uNumOfTables; i++)
{
if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
break;
if (memcmp(tblDir.szTag,"name",4)==0)
{
bFound = TRUE;
tblDir.uLength = SWAPLONG(tblDir.uLength);
tblDir.uOffset = SWAPLONG(tblDir.uOffset);
break;
}
}
if (!bFound)
goto end;
SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
goto end;
ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
bFound = FALSE;
for(i=0; i<ttNTHeader.uNRCount; i++)
{
if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
break;
ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
/* 4 is the Full Font Name */
if(ttRecord.uNameID == 4)
{
int nPos;
LPSTR buf;
static const char tt[] = " (TrueType)";
ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
SetFilePointer(handle, tblDir.uOffset +
ttRecord.uStringOffset +
ttNTHeader.uStorageOffset,
NULL, FILE_BEGIN);
buf = msi_alloc_zero( ttRecord.uStringLength + 1 + strlen(tt) );
ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
if (strlen(buf) > 0)
{
strcat(buf,tt);
ret = strdupAtoW(buf);
msi_free(buf);
break;
}
msi_free(buf);
SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
}
}
end:
CloseHandle(handle);
TRACE("Returning fontname %s\n",debugstr_w(ret));
return ret;
}
static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
{
MSIPACKAGE *package = (MSIPACKAGE*)param;
LPWSTR name;
LPCWSTR filename;
MSIFILE *file;
static const WCHAR regfont1[] =
{'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s',' ','N','T','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'F','o','n','t','s',0};
static const WCHAR regfont2[] =
{'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'F','o','n','t','s',0};
HKEY hkey1;
HKEY hkey2;
MSIRECORD *uirow;
LPWSTR uipath, p;
filename = MSI_RecordGetString( row, 1 );
file = get_loaded_file( package, filename );
if (!file)
{
ERR("Unable to load file\n");
return ERROR_SUCCESS;
}
/* check to make sure that component is installed */
if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
{
TRACE("Skipping: Component not scheduled for install\n");
return ERROR_SUCCESS;
}
RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
if (MSI_RecordIsNull(row,2))
name = load_ttfname_from( file->TargetPath );
else
name = msi_dup_record_field(row,2);
if (name)
{
msi_reg_set_val_str( hkey1, name, file->TargetPath);
msi_reg_set_val_str( hkey2, name, file->TargetPath);
}
msi_free(name);
RegCloseKey(hkey1);
RegCloseKey(hkey2);
/* the UI chunk */
uirow = MSI_CreateRecord( 1 );
uipath = strdupW( file->TargetPath );
p = strrchrW(uipath,'\\');
if (p) p++;
else p = uipath;
MSI_RecordSetStringW( uirow, 1, p );
ui_actiondata( package, szRegisterFonts, uirow);
msiobj_release( &uirow->hdr );
msi_free( uipath );
/* FIXME: call ui_progress? */
return ERROR_SUCCESS;
}
UINT ACTION_RegisterFonts(MSIPACKAGE *package)
{
UINT rc;
MSIQUERY * view;
static const WCHAR ExecSeqQuery[] =
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
'`','F','o','n','t','`',0};
rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
if (rc != ERROR_SUCCESS)
{
TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
return ERROR_SUCCESS;
}
MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
msiobj_release(&view->hdr);
return ERROR_SUCCESS;
}
--- NEW FILE: format.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Mike McCormack for CodeWeavers
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
*/
#include <stdarg.h>
#include <stdio.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "winnls.h"
#include "objbase.h"
#include "oleauto.h"
#include "msipriv.h"
#include "msiserver.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
WCHAR** data, DWORD len, MSIRECORD* record,
BOOL* in_group);
static LPWSTR build_default_format(const MSIRECORD* record)
{
int i;
int count;
LPWSTR rc, buf;
static const WCHAR fmt[] = {'%','i',':',' ','%','s',' ',0};
static const WCHAR fmt_null[] = {'%','i',':',' ',' ',0};
static const WCHAR fmt_index[] = {'%','i',0};
LPCWSTR str;
WCHAR index[10];
DWORD size, max_len, len;
count = MSI_RecordGetFieldCount(record);
max_len = MAX_PATH;
buf = msi_alloc((max_len + 1) * sizeof(WCHAR));
rc = NULL;
size = 1;
for (i = 1; i <= count; i++)
{
sprintfW(index,fmt_index,i);
str = MSI_RecordGetString(record, i);
len = (str) ? lstrlenW(str) : 0;
len += (sizeof(fmt_null) - 3) + lstrlenW(index);
size += len;
if (len > max_len)
{
max_len = len;
buf = msi_realloc(buf, (max_len + 1) * sizeof(WCHAR));
if (!buf) return NULL;
}
if (str)
sprintfW(buf,fmt,i,str);
else
sprintfW(buf,fmt_null,i);
if (!rc)
{
rc = msi_alloc(size * sizeof(WCHAR));
lstrcpyW(rc, buf);
}
else
{
rc = msi_realloc(rc, size * sizeof(WCHAR));
lstrcatW(rc, buf);
}
}
msi_free(buf);
return rc;
}
static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
{
DWORD i;
for (i = 0; i < len; i++)
if (buf[i] == token)
return &buf[i];
return NULL;
}
/* break out helper functions for deformating */
static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
{
LPWSTR value = NULL;
MSICOMPONENT *comp;
BOOL source;
*sz = 0;
if (!package)
return NULL;
comp = get_loaded_component(package,key);
if (comp)
{
source = (comp->Action == INSTALLSTATE_SOURCE) ? TRUE : FALSE;
value = resolve_folder(package, comp->Directory, source, FALSE, TRUE, NULL);
*sz = (strlenW(value)) * sizeof(WCHAR);
}
return value;
}
static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
BOOL shortname)
{
LPWSTR value = NULL;
MSIFILE *file;
*sz = 0;
if (!package)
return NULL;
file = get_loaded_file( package, key );
if (file)
{
if (!shortname)
{
value = strdupW( file->TargetPath );
*sz = (strlenW(value)) * sizeof(WCHAR);
}
else
{
DWORD size = 0;
size = GetShortPathNameW( file->TargetPath, NULL, 0 );
if (size > 0)
{
*sz = (size-1) * sizeof (WCHAR);
size ++;
value = msi_alloc(size * sizeof(WCHAR));
GetShortPathNameW( file->TargetPath, value, size );
}
else
{
value = strdupW( file->TargetPath );
*sz = (lstrlenW(value)) * sizeof(WCHAR);
}
}
}
return value;
}
static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
DWORD* chunk)
{
LPWSTR value = NULL;
DWORD sz;
sz = GetEnvironmentVariableW(key,NULL,0);
if (sz > 0)
{
sz++;
value = msi_alloc(sz * sizeof(WCHAR));
GetEnvironmentVariableW(key,value,sz);
*chunk = (strlenW(value)) * sizeof(WCHAR);
}
else
{
ERR("Unknown environment variable %s\n", debugstr_w(key));
*chunk = 0;
value = NULL;
}
return value;
}
static LPWSTR deformat_NULL(DWORD* chunk)
{
LPWSTR value;
value = msi_alloc(sizeof(WCHAR)*2);
value[0] = 0;
*chunk = sizeof(WCHAR);
return value;
}
static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
{
LPWSTR value;
value = msi_alloc(sizeof(WCHAR)*2);
value[0] = key[0];
*chunk = sizeof(WCHAR);
return value;
}
static BOOL is_key_number(LPCWSTR key)
{
INT index = 0;
if (key[0] == 0)
return FALSE;
while (isdigitW(key[index])) index++;
if (key[index] == 0)
return TRUE;
else
return FALSE;
}
static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
{
INT index;
LPWSTR value;
index = atoiW(key);
TRACE("record index %i\n",index);
value = msi_dup_record_field(record,index);
if (value)
*chunk = strlenW(value) * sizeof(WCHAR);
else
{
value = NULL;
*chunk = 0;
}
return value;
}
static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
{
LPWSTR value;
if (!package)
return NULL;
value = msi_dup_property( package, key );
if (value)
*chunk = (strlenW(value)) * sizeof(WCHAR);
return value;
}
/*
* Groups cannot be nested. They are just treated as from { to next }
*/
static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
LPWSTR *group, LPCWSTR *mark,
LPCWSTR* mark2)
{
int i;
BOOL found = FALSE;
*mark = scanW(source,'{',len_remaining);
if (!*mark)
return FALSE;
for (i = 1; (*mark - source) + i < len_remaining; i++)
{
if ((*mark)[i] == '}')
{
found = TRUE;
break;
}
}
if (! found)
return FALSE;
*mark2 = &(*mark)[i];
i = *mark2 - *mark;
*group = msi_alloc(i*sizeof(WCHAR));
i -= 1;
memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
(*group)[i] = 0;
TRACE("Found group %s\n",debugstr_w(*group));
return TRUE;
}
static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
BOOL *nested)
{
INT count = 0;
INT total_count = 0;
int i;
*mark = scanW(source,'[',len_remaining);
if (!*mark)
return FALSE;
count = 1;
total_count = 1;
*nested = FALSE;
for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
{
if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
{
count ++;
total_count ++;
*nested = TRUE;
}
else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
{
count --;
}
}
if (count > 0)
return FALSE;
*mark2 = &(*mark)[i-1];
i = *mark2 - *mark;
*key = msi_alloc(i*sizeof(WCHAR));
/* do not have the [] in the key */
i -= 1;
memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
(*key)[i] = 0;
TRACE("Found Key %s\n",debugstr_w(*key));
return TRUE;
}
static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
MSIRECORD* record, DWORD* size)
{
LPWSTR value = NULL;
LPCWSTR mark, mark2;
LPWSTR key;
BOOL nested;
INT failcount;
static const WCHAR fmt[] = {'{','%','s','}',0};
UINT sz;
if (!group || group[0] == 0)
{
*size = 0;
return NULL;
}
/* if no [] then group is returned as is */
if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
{
*size = (len+2)*sizeof(WCHAR);
value = msi_alloc(*size);
sprintfW(value,fmt,group);
/* do not return size of the null at the end */
*size = (len+1)*sizeof(WCHAR);
return value;
}
msi_free(key);
failcount = 0;
sz = deformat_string_internal(package, group, &value, strlenW(group),
record, &failcount);
if (failcount==0)
{
*size = sz * sizeof(WCHAR);
return value;
}
else if (failcount < 0)
{
LPWSTR v2;
v2 = msi_alloc((sz+2)*sizeof(WCHAR));
v2[0] = '{';
memcpy(&v2[1],value,sz*sizeof(WCHAR));
v2[sz+1]='}';
msi_free(value);
*size = (sz+2)*sizeof(WCHAR);
return v2;
}
else
{
msi_free(value);
*size = 0;
return NULL;
}
}
/*
* len is in WCHARs
* return is also in WCHARs
*/
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
WCHAR** data, DWORD len, MSIRECORD* record,
INT* failcount)
{
LPCWSTR mark = NULL;
LPCWSTR mark2 = NULL;
DWORD size=0;
DWORD chunk=0;
LPWSTR key;
LPWSTR value = NULL;
DWORD sz;
LPBYTE newdata = NULL;
const WCHAR* progress = NULL;
BOOL nested;
if (ptr==NULL)
{
TRACE("Deformatting NULL string\n");
*data = NULL;
return 0;
}
TRACE("Starting with %s\n",debugstr_wn(ptr,len));
/* scan for special characters... fast exit */
if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
(scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
{
/* not formatted */
*data = msi_alloc((len*sizeof(WCHAR)));
memcpy(*data,ptr,len*sizeof(WCHAR));
TRACE("Returning %s\n",debugstr_wn(*data,len));
return len;
}
progress = ptr;
while (progress - ptr < len)
{
/* seek out first group if existing */
if (find_next_group(progress, len - (progress - ptr), &key,
&mark, &mark2))
{
value = deformat_group(package, key, strlenW(key)+1, record,
&chunk);
msi_free( key );
key = NULL;
nested = FALSE;
}
/* formatted string located */
else if (!find_next_outermost_key(progress, len - (progress - ptr),
&key, &mark, &mark2, &nested))
{
LPBYTE nd2;
TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
size/sizeof(WCHAR)));
chunk = (len - (progress - ptr)) * sizeof(WCHAR);
TRACE("after chunk is %i + %i\n",size,chunk);
if (size)
nd2 = msi_realloc(newdata,(size+chunk));
else
nd2 = msi_alloc(chunk);
newdata = nd2;
memcpy(&newdata[size],progress,chunk);
size+=chunk;
break;
}
if (mark != progress)
{
LPBYTE tgt;
DWORD old_size = size;
INT cnt = (mark - progress);
TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
size += cnt * sizeof(WCHAR);
if (!old_size)
tgt = msi_alloc(size);
else
tgt = msi_realloc(newdata,size);
newdata = tgt;
memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
}
progress = mark;
if (nested)
{
TRACE("Nested key... %s\n",debugstr_w(key));
deformat_string_internal(package, key, &value, strlenW(key)+1,
record, failcount);
msi_free(key);
key = value;
}
TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
size/sizeof(WCHAR)),debugstr_w(key));
if (!package)
{
/* only deformat number indexs */
if (key && is_key_number(key))
{
value = deformat_index(record,key,&chunk);
if (!chunk && failcount && *failcount >= 0)
(*failcount)++;
}
else
{
if (failcount)
*failcount = -1;
if(key)
{
DWORD keylen = strlenW(key);
chunk = (keylen + 2)*sizeof(WCHAR);
value = msi_alloc(chunk);
value[0] = '[';
memcpy(&value[1],key,keylen*sizeof(WCHAR));
value[1+keylen] = ']';
}
}
}
else
{
sz = 0;
if (key) switch (key[0])
{
case '~':
value = deformat_NULL(&chunk);
break;
case '$':
value = deformat_component(package,&key[1],&chunk);
break;
case '#':
value = deformat_file(package,&key[1], &chunk, FALSE);
break;
case '!': /* should be short path */
value = deformat_file(package,&key[1], &chunk, TRUE);
break;
case '\\':
value = deformat_escape(&key[1],&chunk);
break;
case '%':
value = deformat_environment(package,&key[1],&chunk);
break;
default:
/* index keys cannot be nested */
if (is_key_number(key))
if (!nested)
value = deformat_index(record,key,&chunk);
else
{
static const WCHAR fmt[] = {'[','%','s',']',0};
value = msi_alloc(10);
sprintfW(value,fmt,key);
chunk = strlenW(value)*sizeof(WCHAR);
}
else
value = deformat_property(package,key,&chunk);
break;
}
}
msi_free(key);
if (value!=NULL)
{
LPBYTE nd2;
TRACE("value %s, chunk %i size %i\n",debugstr_w((LPWSTR)value),
chunk, size);
if (size)
nd2= msi_realloc(newdata,(size + chunk));
else
nd2= msi_alloc(chunk);
newdata = nd2;
memcpy(&newdata[size],value,chunk);
size+=chunk;
msi_free(value);
}
else if (failcount && *failcount >=0 )
(*failcount)++;
progress = mark2+1;
}
TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
size/sizeof(WCHAR)));
*data = (LPWSTR)newdata;
return size / sizeof(WCHAR);
}
UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
DWORD *size )
{
LPWSTR deformated;
LPWSTR rec;
DWORD len;
UINT rc = ERROR_INVALID_PARAMETER;
TRACE("%p %p %p %i\n", package, record ,buffer, *size);
rec = msi_dup_record_field(record,0);
if (!rec)
rec = build_default_format(record);
TRACE("(%s)\n",debugstr_w(rec));
len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
record, NULL);
if (buffer)
{
if (*size>len)
{
memcpy(buffer,deformated,len*sizeof(WCHAR));
rc = ERROR_SUCCESS;
buffer[len] = 0;
}
else
{
if (*size > 0)
{
memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
buffer[(*size)-1] = 0;
}
rc = ERROR_MORE_DATA;
}
}
else
rc = ERROR_SUCCESS;
*size = len;
msi_free(rec);
msi_free(deformated);
return rc;
}
UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
LPWSTR szResult, DWORD *sz )
{
UINT r = ERROR_INVALID_HANDLE;
MSIPACKAGE *package;
MSIRECORD *record;
TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
if (!package)
{
HRESULT hr;
IWineMsiRemotePackage *remote_package;
BSTR value = NULL;
DWORD len;
awstring wstr;
remote_package = (IWineMsiRemotePackage *)msi_get_remote( hInstall );
if (remote_package)
{
len = 0;
hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
NULL, &len );
if (FAILED(hr))
goto done;
len++;
value = SysAllocStringLen( NULL, len );
if (!value)
{
r = ERROR_OUTOFMEMORY;
goto done;
}
hr = IWineMsiRemotePackage_FormatRecord( remote_package, hRecord,
value, &len );
if (FAILED(hr))
goto done;
wstr.unicode = TRUE;
wstr.str.w = szResult;
r = msi_strcpy_to_awstring( value, &wstr, sz );
done:
IWineMsiRemotePackage_Release( remote_package );
SysFreeString( value );
if (FAILED(hr))
{
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
return HRESULT_CODE(hr);
return ERROR_FUNCTION_FAILED;
}
return r;
}
}
record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
if (!record)
return ERROR_INVALID_HANDLE;
if (!sz)
{
msiobj_release( &record->hdr );
if (szResult)
return ERROR_INVALID_PARAMETER;
else
return ERROR_SUCCESS;
}
r = MSI_FormatRecordW( package, record, szResult, sz );
msiobj_release( &record->hdr );
if (package)
msiobj_release( &package->hdr );
return r;
}
UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
LPSTR szResult, DWORD *sz )
{
UINT r;
DWORD len, save;
LPWSTR value;
TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
if (!hRecord)
return ERROR_INVALID_HANDLE;
if (!sz)
{
if (szResult)
return ERROR_INVALID_PARAMETER;
else
return ERROR_SUCCESS;
}
r = MsiFormatRecordW( hInstall, hRecord, NULL, &len );
if (r != ERROR_SUCCESS)
return r;
value = msi_alloc(++len * sizeof(WCHAR));
if (!value)
return ERROR_OUTOFMEMORY;
r = MsiFormatRecordW( hInstall, hRecord, value, &len );
if (r != ERROR_SUCCESS)
goto done;
save = len + 1;
len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, value, -1, szResult, *sz, NULL, NULL);
if (szResult && len > *sz)
{
if (*sz) szResult[*sz - 1] = '\0';
r = ERROR_MORE_DATA;
}
*sz = save - 1;
done:
msi_free(value);
return r;
}
--- NEW FILE: handle.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "shlwapi.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "msipriv.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static CRITICAL_SECTION MSI_handle_cs;
static CRITICAL_SECTION_DEBUG MSI_handle_cs_debug =
{
0, 0, &MSI_handle_cs,
{ &MSI_handle_cs_debug.ProcessLocksList,
&MSI_handle_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": MSI_handle_cs") }
};
static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 };
static CRITICAL_SECTION MSI_object_cs;
static CRITICAL_SECTION_DEBUG MSI_object_cs_debug =
{
0, 0, &MSI_object_cs,
{ &MSI_object_cs_debug.ProcessLocksList,
&MSI_object_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": MSI_object_cs") }
};
static CRITICAL_SECTION MSI_object_cs = { &MSI_object_cs_debug, -1, 0, 0, 0, 0 };
typedef struct msi_handle_info_t
{
BOOL remote;
union {
MSIOBJECTHDR *obj;
IUnknown *unk;
} u;
DWORD dwThreadId;
} msi_handle_info;
static msi_handle_info *msihandletable = NULL;
static unsigned int msihandletable_size = 0;
void msi_free_handle_table(void)
{
msi_free( msihandletable );
msihandletable = NULL;
msihandletable_size = 0;
}
static MSIHANDLE alloc_handle_table_entry(void)
{
UINT i;
/* find a slot */
for(i=0; i<msihandletable_size; i++)
if( !msihandletable[i].u.obj && !msihandletable[i].u.unk )
break;
if( i==msihandletable_size )
{
msi_handle_info *p;
int newsize;
if (msihandletable_size == 0)
{
newsize = 256;
p = msi_alloc_zero(newsize*sizeof(msi_handle_info));
}
else
{
newsize = msihandletable_size * 2;
p = msi_realloc_zero(msihandletable,
newsize*sizeof(msi_handle_info));
}
if (!p)
return 0;
msihandletable = p;
msihandletable_size = newsize;
}
return i + 1;
}
MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj )
{
msi_handle_info *entry;
MSIHANDLE ret;
EnterCriticalSection( &MSI_handle_cs );
ret = alloc_handle_table_entry();
if (ret)
{
entry = &msihandletable[ ret - 1 ];
msiobj_addref( obj );
entry->u.obj = obj;
entry->dwThreadId = GetCurrentThreadId();
entry->remote = FALSE;
}
LeaveCriticalSection( &MSI_handle_cs );
TRACE("%p -> %ld\n", obj, ret );
return ret;
}
MSIHANDLE alloc_msi_remote_handle( IUnknown *unk )
{
msi_handle_info *entry;
MSIHANDLE ret;
EnterCriticalSection( &MSI_handle_cs );
ret = alloc_handle_table_entry();
if (ret)
{
entry = &msihandletable[ ret - 1 ];
IUnknown_AddRef( unk );
entry->u.unk = unk;
entry->dwThreadId = GetCurrentThreadId();
entry->remote = TRUE;
}
LeaveCriticalSection( &MSI_handle_cs );
TRACE("%p -> %ld\n", unk, ret);
return ret;
}
void *msihandle2msiinfo(MSIHANDLE handle, UINT type)
{
MSIOBJECTHDR *ret = NULL;
EnterCriticalSection( &MSI_handle_cs );
handle--;
if( handle >= msihandletable_size )
goto out;
if( msihandletable[handle].remote)
goto out;
if( !msihandletable[handle].u.obj )
goto out;
if( msihandletable[handle].u.obj->magic != MSIHANDLE_MAGIC )
goto out;
if( type && (msihandletable[handle].u.obj->type != type) )
goto out;
ret = msihandletable[handle].u.obj;
msiobj_addref( ret );
out:
LeaveCriticalSection( &MSI_handle_cs );
return (void*) ret;
}
IUnknown *msi_get_remote( MSIHANDLE handle )
{
IUnknown *unk = NULL;
EnterCriticalSection( &MSI_handle_cs );
handle--;
if( handle>=msihandletable_size )
goto out;
if( !msihandletable[handle].remote)
goto out;
unk = msihandletable[handle].u.unk;
if( unk )
IUnknown_AddRef( unk );
out:
LeaveCriticalSection( &MSI_handle_cs );
return unk;
}
void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy )
{
MSIOBJECTHDR *info;
info = msi_alloc_zero( size );
if( info )
{
info->magic = MSIHANDLE_MAGIC;
info->type = type;
info->refcount = 1;
info->destructor = destroy;
}
return info;
}
void msiobj_addref( MSIOBJECTHDR *info )
{
if( !info )
return;
if( info->magic != MSIHANDLE_MAGIC )
{
ERR("Invalid handle!\n");
return;
}
InterlockedIncrement(&info->refcount);
}
void msiobj_lock( MSIOBJECTHDR *info )
{
EnterCriticalSection( &MSI_object_cs );
}
void msiobj_unlock( MSIOBJECTHDR *info )
{
LeaveCriticalSection( &MSI_object_cs );
}
int msiobj_release( MSIOBJECTHDR *info )
{
int ret;
if( !info )
return -1;
if( info->magic != MSIHANDLE_MAGIC )
{
ERR("Invalid handle!\n");
return -1;
}
ret = InterlockedDecrement( &info->refcount );
if( ret==0 )
{
if( info->destructor )
info->destructor( info );
msi_free( info );
TRACE("object %p destroyed\n", info);
}
return ret;
}
/***********************************************************
* MsiCloseHandle [MSI.@]
*/
UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
{
MSIOBJECTHDR *info = NULL;
UINT ret = ERROR_INVALID_HANDLE;
TRACE("%lx\n",handle);
if (!handle)
return ERROR_SUCCESS;
EnterCriticalSection( &MSI_handle_cs );
handle--;
if (handle >= msihandletable_size)
goto out;
if (msihandletable[handle].remote)
{
IUnknown_Release( msihandletable[handle].u.unk );
}
else
{
info = msihandletable[handle].u.obj;
if( !info )
goto out;
if( info->magic != MSIHANDLE_MAGIC )
{
ERR("Invalid handle!\n");
goto out;
}
}
msihandletable[handle].u.obj = NULL;
msihandletable[handle].remote = 0;
msihandletable[handle].dwThreadId = 0;
ret = ERROR_SUCCESS;
TRACE("handle %lx destroyed\n", handle+1);
out:
LeaveCriticalSection( &MSI_handle_cs );
if( info )
msiobj_release( info );
return ret;
}
/***********************************************************
* MsiCloseAllHandles [MSI.@]
*
* Closes all handles owned by the current thread
*
* RETURNS:
* The number of handles closed
*/
UINT WINAPI MsiCloseAllHandles(void)
{
UINT i, n=0;
TRACE("\n");
EnterCriticalSection( &MSI_handle_cs );
for(i=0; i<msihandletable_size; i++)
{
if(msihandletable[i].dwThreadId == GetCurrentThreadId())
{
LeaveCriticalSection( &MSI_handle_cs );
MsiCloseHandle( i+1 );
EnterCriticalSection( &MSI_handle_cs );
n++;
}
}
LeaveCriticalSection( &MSI_handle_cs );
return n;
}
--- NEW FILE: helpers.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1028 lines suppressed...]
msiobj_release( &rec->hdr );
data = NULL;
return data;
}
void msi_ui_error( DWORD msg_id, DWORD type )
{
WCHAR text[2048];
static const WCHAR title[] = {
'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
};
if (!MsiLoadStringW( -1, msg_id, text, sizeof(text) / sizeof(text[0]),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) ))
return;
MessageBoxW( NULL, text, title, type );
}
--- NEW FILE: insert.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSIINSERTVIEW
{
MSIVIEW view;
MSIDATABASE *db;
BOOL bIsTemp;
MSIVIEW *sv;
column_info *vals;
} MSIINSERTVIEW;
static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
TRACE("%p %d %d %p\n", iv, row, col, val );
return ERROR_FUNCTION_FAILED;
}
/*
* msi_query_merge_record
*
* Merge a value_list and a record to create a second record.
* Replace wildcard entries in the valuelist with values from the record
*/
MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec )
{
MSIRECORD *merged;
DWORD wildcard_count = 1, i;
merged = MSI_CreateRecord( fields );
for( i=1; i <= fields; i++ )
{
if( !vl )
{
TRACE("Not enough elements in the list to insert\n");
goto err;
}
switch( vl->val->type )
{
case EXPR_SVAL:
TRACE("field %d -> %s\n", i, debugstr_w(vl->val->u.sval));
MSI_RecordSetStringW( merged, i, vl->val->u.sval );
break;
case EXPR_IVAL:
MSI_RecordSetInteger( merged, i, vl->val->u.ival );
break;
case EXPR_WILDCARD:
if( !rec )
goto err;
MSI_RecordCopyField( rec, wildcard_count, merged, i );
wildcard_count++;
break;
default:
ERR("Unknown expression type %d\n", vl->val->type);
}
vl = vl->next;
}
return merged;
err:
msiobj_release( &merged->hdr );
return NULL;
}
static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
UINT r, col_count = 0;
MSIVIEW *sv;
MSIRECORD *values = NULL;
TRACE("%p %p\n", iv, record );
sv = iv->sv;
if( !sv )
return ERROR_FUNCTION_FAILED;
r = sv->ops->execute( sv, 0 );
TRACE("tv execute returned %x\n", r);
if( r )
return r;
r = sv->ops->get_dimensions( sv, NULL, &col_count );
if( r )
goto err;
/*
* Merge the wildcard values into the list of values provided
* in the query, and create a record containing both.
*/
values = msi_query_merge_record( col_count, iv->vals, record );
if( !values )
goto err;
r = sv->ops->insert_row( sv, values, iv->bIsTemp );
err:
if( values )
msiobj_release( &values->hdr );
return r;
}
static UINT INSERT_close( struct tagMSIVIEW *view )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
MSIVIEW *sv;
TRACE("%p\n", iv);
sv = iv->sv;
if( !sv )
return ERROR_FUNCTION_FAILED;
return sv->ops->close( sv );
}
static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
MSIVIEW *sv;
TRACE("%p %p %p\n", iv, rows, cols );
sv = iv->sv;
if( !sv )
return ERROR_FUNCTION_FAILED;
return sv->ops->get_dimensions( sv, rows, cols );
}
static UINT INSERT_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
MSIVIEW *sv;
TRACE("%p %d %p %p\n", iv, n, name, type );
sv = iv->sv;
if( !sv )
return ERROR_FUNCTION_FAILED;
return sv->ops->get_column_info( sv, n, name, type );
}
static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec)
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
TRACE("%p %d %p\n", iv, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT INSERT_delete( struct tagMSIVIEW *view )
{
MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
MSIVIEW *sv;
TRACE("%p\n", iv );
sv = iv->sv;
if( sv )
sv->ops->delete( sv );
msiobj_release( &iv->db->hdr );
msi_free( iv );
return ERROR_SUCCESS;
}
static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
return ERROR_FUNCTION_FAILED;
}
static const MSIVIEWOPS insert_ops =
{
INSERT_fetch_int,
NULL,
NULL,
NULL,
NULL,
INSERT_execute,
INSERT_close,
INSERT_get_dimensions,
INSERT_get_column_info,
INSERT_modify,
INSERT_delete,
INSERT_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
static UINT count_column_info( const column_info *ci )
{
UINT n = 0;
for ( ; ci; ci = ci->next )
n++;
return n;
}
UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
column_info *columns, column_info *values, BOOL temp )
{
MSIINSERTVIEW *iv = NULL;
UINT r;
MSIVIEW *tv = NULL, *sv = NULL;
TRACE("%p\n", iv );
/* there should be one value for each column */
if ( count_column_info( columns ) != count_column_info(values) )
return ERROR_BAD_QUERY_SYNTAX;
r = TABLE_CreateView( db, table, &tv );
if( r != ERROR_SUCCESS )
return r;
r = SELECT_CreateView( db, &sv, tv, columns );
if( r != ERROR_SUCCESS )
{
if( tv )
tv->ops->delete( tv );
return r;
}
iv = msi_alloc_zero( sizeof *iv );
if( !iv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
iv->view.ops = &insert_ops;
msiobj_addref( &db->hdr );
iv->db = db;
iv->vals = values;
iv->bIsTemp = temp;
iv->sv = sv;
*view = (MSIVIEW*) iv;
return ERROR_SUCCESS;
}
--- NEW FILE: install.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1215 lines suppressed...]
hInstall, debugstr_w(szFeature), pInstallState, pInstallState ? *pInstallState : 0);
return ERROR_SUCCESS;
}
/***********************************************************************
* MsiGetFeatureValidStatesA (MSI.@)
*/
UINT WINAPI MsiGetFeatureValidStatesA(MSIHANDLE hInstall, LPCSTR szFeature,
DWORD* pInstallState)
{
UINT ret;
LPWSTR szwFeature = strdupAtoW(szFeature);
ret = MsiGetFeatureValidStatesW(hInstall, szwFeature, pInstallState);
msi_free(szwFeature);
return ret;
}
--- NEW FILE: join.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2006 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
typedef struct tagMSIJOINVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *left, *right;
UINT left_count, right_count;
UINT left_rows, right_rows;
} MSIJOINVIEW;
static UINT JOIN_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
MSIVIEW *table;
TRACE("%p %d %d %p\n", jv, row, col, val );
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
if( (col==0) || (col>(jv->left_count + jv->right_count)) )
return ERROR_FUNCTION_FAILED;
if( row >= (jv->left_rows * jv->right_rows) )
return ERROR_FUNCTION_FAILED;
if( col <= jv->left_count )
{
table = jv->left;
row = (row/jv->right_rows);
}
else
{
table = jv->right;
row = (row % jv->right_rows);
col -= jv->left_count;
}
return table->ops->fetch_int( table, row, col, val );
}
static UINT JOIN_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
MSIVIEW *table;
TRACE("%p %d %d %p\n", jv, row, col, stm );
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
if( (col==0) || (col>(jv->left_count + jv->right_count)) )
return ERROR_FUNCTION_FAILED;
if( row >= jv->left_rows * jv->right_rows )
return ERROR_FUNCTION_FAILED;
if( row <= jv->left_count )
{
table = jv->left;
row = (row/jv->right_rows);
}
else
{
table = jv->right;
row = (row % jv->right_rows);
col -= jv->left_count;
}
return table->ops->fetch_stream( table, row, col, stm );
}
static UINT JOIN_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
UINT r, *ldata = NULL, *rdata = NULL;
TRACE("%p %p\n", jv, record);
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
r = jv->left->ops->execute( jv->left, NULL );
if (r != ERROR_SUCCESS)
return r;
r = jv->right->ops->execute( jv->right, NULL );
if (r != ERROR_SUCCESS)
return r;
/* get the number of rows in each table */
r = jv->left->ops->get_dimensions( jv->left, &jv->left_rows, NULL );
if( r != ERROR_SUCCESS )
{
ERR("can't get left table dimensions\n");
goto end;
}
r = jv->right->ops->get_dimensions( jv->right, &jv->right_rows, NULL );
if( r != ERROR_SUCCESS )
{
ERR("can't get right table dimensions\n");
goto end;
}
end:
msi_free( ldata );
msi_free( rdata );
return r;
}
static UINT JOIN_close( struct tagMSIVIEW *view )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
TRACE("%p\n", jv );
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
jv->left->ops->close( jv->left );
jv->right->ops->close( jv->right );
return ERROR_SUCCESS;
}
static UINT JOIN_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
TRACE("%p %p %p\n", jv, rows, cols );
if( cols )
*cols = jv->left_count + jv->right_count;
if( rows )
{
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
*rows = jv->left_rows * jv->right_rows;
}
return ERROR_SUCCESS;
}
static UINT JOIN_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
TRACE("%p %d %p %p\n", jv, n, name, type );
if( !jv->left || !jv->right )
return ERROR_FUNCTION_FAILED;
if( (n==0) || (n>(jv->left_count + jv->right_count)) )
return ERROR_FUNCTION_FAILED;
if( n <= jv->left_count )
return jv->left->ops->get_column_info( jv->left, n, name, type );
n = n - jv->left_count;
return jv->right->ops->get_column_info( jv->right, n, name, type );
}
static UINT JOIN_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
TRACE("%p %d %p\n", jv, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT JOIN_delete( struct tagMSIVIEW *view )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
TRACE("%p\n", jv );
if( jv->left )
jv->left->ops->delete( jv->left );
jv->left = NULL;
if( jv->right )
jv->right->ops->delete( jv->right );
jv->right = NULL;
msi_free( jv );
return ERROR_SUCCESS;
}
static UINT JOIN_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
MSIJOINVIEW *jv = (MSIJOINVIEW*)view;
FIXME("%p, %d, %u, %p\n", jv, col, val, *handle);
return ERROR_FUNCTION_FAILED;
}
static const MSIVIEWOPS join_ops =
{
JOIN_fetch_int,
JOIN_fetch_stream,
NULL,
NULL,
NULL,
JOIN_execute,
JOIN_close,
JOIN_get_dimensions,
JOIN_get_column_info,
JOIN_modify,
JOIN_delete,
JOIN_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
UINT JOIN_CreateView( MSIDATABASE *db, MSIVIEW **view,
LPCWSTR left, LPCWSTR right )
{
MSIJOINVIEW *jv = NULL;
UINT r = ERROR_SUCCESS;
TRACE("%p (%s,%s)\n", jv, debugstr_w(left), debugstr_w(right) );
jv = msi_alloc_zero( sizeof *jv );
if( !jv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
jv->view.ops = &join_ops;
jv->db = db;
/* create the tables to join */
r = TABLE_CreateView( db, left, &jv->left );
if( r != ERROR_SUCCESS )
{
ERR("can't create left table\n");
goto end;
}
r = TABLE_CreateView( db, right, &jv->right );
if( r != ERROR_SUCCESS )
{
ERR("can't create right table\n");
goto end;
}
/* get the number of columns in each table */
r = jv->left->ops->get_dimensions( jv->left, NULL, &jv->left_count );
if( r != ERROR_SUCCESS )
{
ERR("can't get left table dimensions\n");
goto end;
}
r = jv->right->ops->get_dimensions( jv->right, NULL, &jv->right_count );
if( r != ERROR_SUCCESS )
{
ERR("can't get right table dimensions\n");
goto end;
}
*view = &jv->view;
return ERROR_SUCCESS;
end:
jv->view.ops->delete( &jv->view );
return r;
}
--- NEW FILE: Makefile.in ---
EXTRADEFS = -D__WINESRC__ -DSTRICT
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = msi
EXTRALIBS = $(LIBUUID)
LDDLLFLAGS = @LDDLLFLAGS@
SYMBOLFILE = $(MODULE).tmp.o
C_SRCS = \
action.c \
alter.c \
appsearch.c \
automation.c \
classes.c \
create.c \
custom.c \
database.c \
delete.c \
dialog.c \
distinct.c \
events.c \
files.c \
font.c \
format.c \
handle.c \
helpers.c \
insert.c \
install.c \
join.c \
msi.c \
msi_main.c \
msiquery.c \
order.c \
package.c \
preview.c \
record.c \
registry.c \
regsvr.c \
script.c \
select.c \
source.c \
streams.c \
string.c \
suminfo.c \
table.c \
tokenize.c \
update.c \
upgrade.c \
where.c
IDL_H_SRCS = msiserver.idl
IDL_I_SRCS = msiserver.idl
IDL_TLB_SRCS = msiserver.idl
BISON_SRCS = \
cond.y \
sql.y
RC_SRCS = msi.rc
@MAKE_DLL_RULES@
sql.tab.c: sql.tab.h # for parallel makes
### Dependencies:
tokenize.o: sql.tab.h
--- NEW FILE: msi.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...2181 lines suppressed...]
* MsiAdvertiseScriptW [MSI.@]
*/
UINT WINAPI MsiAdvertiseScriptW( LPCWSTR szScriptFile, DWORD dwFlags,
PHKEY phRegData, BOOL fRemoveItems )
{
FIXME("%s %08x %p %d\n",
debugstr_w( szScriptFile ), dwFlags, phRegData, fRemoveItems );
return ERROR_CALL_NOT_IMPLEMENTED;
}
/***********************************************************************
* MsiAdvertiseScriptA [MSI.@]
*/
UINT WINAPI MsiAdvertiseScriptA( LPCSTR szScriptFile, DWORD dwFlags,
PHKEY phRegData, BOOL fRemoveItems )
{
FIXME("%s %08x %p %d\n",
debugstr_a( szScriptFile ), dwFlags, phRegData, fRemoveItems );
return ERROR_CALL_NOT_IMPLEMENTED;
}
--- NEW FILE: msi.rc ---
/*
* Resources for MSI
*
* Copyright 2005 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winnls.h"
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#include "version.rc"
#include "msi_Bg.rc"
#include "msi_De.rc"
#include "msi_En.rc"
#include "msi_Eo.rc"
#include "msi_Es.rc"
#include "msi_Fi.rc"
#include "msi_Fr.rc"
#include "msi_Hu.rc"
#include "msi_It.rc"
#include "msi_Ko.rc"
#include "msi_Nl.rc"
#include "msi_No.rc"
#include "msi_Pl.rc"
#include "msi_Pt.rc"
#include "msi_Ru.rc"
#include "msi_Tr.rc"
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
1 TYPELIB LOADONCALL DISCARDABLE msiserver.tlb
/* BINRES instadvert.bmp */
0x1001 BITMAP
{
'42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00'
'00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00'
'00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00'
'00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80'
'00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80'
'00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF'
'00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF'
'00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 70 00 00 07 77 77 77 77'
'77 77 77 77 77 77 77 70 03 33 B3 30 07 77 77 77'
'77 77 77 77 77 77 77 00 B3 33 33 33 00 77 77 77'
'77 77 77 77 77 77 77 03 33 B3 A3 B3 30 77 77 77'
'77 77 07 77 77 77 70 33 33 33 33 33 B3 07 77 77'
'77 70 00 77 77 77 70 33 B3 30 03 33 33 07 77 77'
'77 00 00 07 77 77 70 33 33 00 00 3B 33 07 77 77'
'70 00 00 00 77 77 70 33 B3 30 0B 33 3B 07 77 77'
'00 00 00 00 07 77 70 3B 3A 33 33 33 33 07 77 70'
'00 00 00 00 00 77 77 03 3B 3B 3A 3B 30 77 77 77'
'77 77 77 77 77 77 77 00 33 33 3B 33 00 77 77 77'
'77 77 77 77 77 77 77 70 03 B3 33 30 07 77 77 77'
'77 77 77 77 77 77 77 77 70 00 00 07 77 77 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77'
}
/* BINRES instabsent.bmp */
0x1002 BITMAP
{
'42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00'
'00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00'
'00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00'
'00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80'
'00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80'
'00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF'
'00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF'
'00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 70 07 77 77 77 77 70 07 77 77'
'77 77 77 77 77 77 70 00 77 77 77 77 00 07 77 77'
'77 77 77 77 77 77 77 00 07 77 77 70 00 77 77 77'
'77 77 77 77 77 77 77 70 00 77 77 00 07 77 77 77'
'77 77 77 77 77 77 77 77 00 07 70 00 77 77 77 77'
'77 77 07 77 77 77 77 77 70 00 00 07 77 77 77 77'
'77 70 00 77 77 77 77 77 77 00 00 77 77 77 77 77'
'77 00 00 07 77 77 77 77 77 00 00 77 77 77 77 77'
'70 00 00 00 77 77 77 77 70 00 00 07 77 77 77 77'
'00 00 00 00 07 77 77 77 00 07 70 00 77 77 77 70'
'00 00 00 00 00 77 77 70 00 77 77 00 07 77 77 77'
'77 77 77 77 77 77 77 00 07 77 77 70 00 77 77 77'
'77 77 77 77 77 77 70 00 77 77 77 77 00 07 77 77'
'77 77 77 77 77 77 70 07 77 77 77 77 70 07 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77'
}
/* BINRES instlocal.bmp */
0x1003 BITMAP
{
'42 4D 76 01 00 00 00 00 00 00 76 00 00 00 28 00'
'00 00 20 00 00 00 10 00 00 00 01 00 04 00 00 00'
'00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00'
'00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80'
'00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80'
'00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF'
'00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF'
'00 00 FF FF FF 00 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 70 07 77 77 77 77 77 77'
'77 77 77 77 77 77 77 77 0F F0 77 77 77 77 77 77'
'77 77 77 77 77 77 77 70 FF FF 07 77 77 77 77 77'
'77 77 07 77 77 77 77 0F F0 0F F0 77 77 77 77 77'
'77 70 00 77 77 77 77 0F 07 70 FF 07 77 77 77 77'
'77 00 00 07 77 77 77 70 77 77 0F F0 77 77 77 77'
'70 00 00 00 77 77 77 77 77 77 70 FF 07 77 77 77'
'00 00 00 00 07 77 77 77 77 77 77 0F F0 77 77 70'
'00 00 00 00 00 77 77 77 77 77 77 70 FF 07 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 0F F0 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 70 F0 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 07 77 77'
'77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77'
'77 77 77 77 77 77'
}
--- NEW FILE: msi.spec ---
name msi
type win32
init DllMain
rsrc msi.res
import urlmon.dll
import wininet.dll
import comctl32.dll
import shell32.dll
import shlwapi.dll
import cabinet.dll
import oleaut32.dll
import ole32.dll
import version.dll
import user32.dll
import gdi32.dll
import advapi32.dll
import kernel32.dll
import ntdll.dll
import -delay odbccp32.dll
5 stdcall MsiAdvertiseProductA(str str str long) MsiAdvertiseProductA
6 stdcall MsiAdvertiseProductW(wstr wstr wstr long) MsiAdvertiseProductW
7 stdcall MsiCloseAllHandles() MsiCloseAllHandles
8 stdcall MsiCloseHandle(long) MsiCloseHandle
9 stdcall MsiCollectUserInfoA(str) MsiCollectUserInfoA
10 stdcall MsiCollectUserInfoW(wstr) MsiCollectUserInfoW
11 stdcall MsiConfigureFeatureA(str str long) MsiConfigureFeatureA
12 stub MsiConfigureFeatureFromDescriptorA
13 stub MsiConfigureFeatureFromDescriptorW
14 stdcall MsiConfigureFeatureW(wstr wstr ptr) MsiConfigureFeatureW
15 stdcall MsiConfigureProductA(str long long) MsiConfigureProductA
16 stdcall MsiConfigureProductW(wstr long long) MsiConfigureProductW
17 stdcall MsiCreateRecord(long) MsiCreateRecord
18 stdcall MsiDatabaseApplyTransformA(long str long) MsiDatabaseApplyTransformA
19 stdcall MsiDatabaseApplyTransformW(long wstr long) MsiDatabaseApplyTransformW
20 stdcall MsiDatabaseCommit(long) MsiDatabaseCommit
21 stdcall MsiDatabaseExportA(long str str str) MsiDatabaseExportA
22 stdcall MsiDatabaseExportW(long wstr wstr wstr) MsiDatabaseExportW
23 stdcall MsiDatabaseGenerateTransformA(long long str long long) MsiDatabaseGenerateTransformA
24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long) MsiDatabaseGenerateTransformW
25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr) MsiDatabaseGetPrimaryKeysA
26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr) MsiDatabaseGetPrimaryKeysW
27 stdcall MsiDatabaseImportA(str str long) MsiDatabaseImportA
28 stdcall MsiDatabaseImportW(wstr wstr long) MsiDatabaseImportW
29 stub MsiDatabaseMergeA
30 stub MsiDatabaseMergeW
31 stdcall MsiDatabaseOpenViewA(long str ptr) MsiDatabaseOpenViewA
32 stdcall MsiDatabaseOpenViewW(long wstr ptr) MsiDatabaseOpenViewW
33 stdcall MsiDoActionA(long str) MsiDoActionA
34 stdcall MsiDoActionW(long wstr) MsiDoActionW
35 stdcall MsiEnableUIPreview(long ptr) MsiEnableUIPreview
36 stdcall MsiEnumClientsA(str long ptr) MsiEnumClientsA
37 stdcall MsiEnumClientsW(wstr long ptr) MsiEnumClientsW
38 stdcall MsiEnumComponentQualifiersA(str long ptr ptr ptr ptr) MsiEnumComponentQualifiersA
39 stdcall MsiEnumComponentQualifiersW(wstr long ptr ptr ptr ptr) MsiEnumComponentQualifiersW
40 stdcall MsiEnumComponentsA(long ptr) MsiEnumComponentsA
41 stdcall MsiEnumComponentsW(long ptr) MsiEnumComponentsW
42 stdcall MsiEnumFeaturesA(str long ptr ptr) MsiEnumFeaturesA
43 stdcall MsiEnumFeaturesW(wstr long ptr ptr) MsiEnumFeaturesW
44 stdcall MsiEnumProductsA(long ptr) MsiEnumProductsA
45 stdcall MsiEnumProductsW(long ptr) MsiEnumProductsW
46 stdcall MsiEvaluateConditionA(long str) MsiEvaluateConditionA
47 stdcall MsiEvaluateConditionW(long wstr) MsiEvaluateConditionW
48 stdcall MsiGetLastErrorRecord() MsiGetLastErrorRecord
49 stdcall MsiGetActiveDatabase(long) MsiGetActiveDatabase
50 stdcall MsiGetComponentStateA(long str ptr ptr) MsiGetComponentStateA
51 stdcall MsiGetComponentStateW(long wstr ptr ptr) MsiGetComponentStateW
52 stdcall MsiGetDatabaseState(long) MsiGetDatabaseState
53 stdcall MsiGetFeatureCostA(long str long long ptr) MsiGetFeatureCostA
54 stdcall MsiGetFeatureCostW(long wstr long long ptr) MsiGetFeatureCostW
55 stub MsiGetFeatureInfoA
56 stub MsiGetFeatureInfoW
57 stdcall MsiGetFeatureStateA(long str ptr ptr) MsiGetFeatureStateA
58 stdcall MsiGetFeatureStateW(long wstr ptr ptr) MsiGetFeatureStateW
59 stdcall MsiGetFeatureUsageA(str str ptr ptr) MsiGetFeatureUsageA
60 stdcall MsiGetFeatureUsageW(wstr wstr ptr ptr) MsiGetFeatureUsageW
61 stdcall MsiGetFeatureValidStatesA(long str ptr) MsiGetFeatureValidStatesA
62 stdcall MsiGetFeatureValidStatesW(long wstr ptr) MsiGetFeatureValidStatesW
63 stdcall MsiGetLanguage(long) MsiGetLanguage
64 stdcall MsiGetMode(long long) MsiGetMode
65 stdcall MsiGetProductCodeA(str str) MsiGetProductCodeA
66 stdcall MsiGetProductCodeW(wstr wstr) MsiGetProductCodeW
67 stdcall MsiGetProductInfoA(str str str long) MsiGetProductInfoA
68 stub MsiGetProductInfoFromScriptA
69 stub MsiGetProductInfoFromScriptW
70 stdcall MsiGetProductInfoW(wstr wstr wstr long) MsiGetProductInfoW
71 stdcall MsiGetProductPropertyA(long str ptr ptr) MsiGetProductPropertyA
72 stdcall MsiGetProductPropertyW(long wstr ptr ptr) MsiGetProductPropertyW
73 stdcall MsiGetPropertyA(ptr str ptr ptr) MsiGetPropertyA
74 stdcall MsiGetPropertyW(ptr wstr ptr ptr) MsiGetPropertyW
75 stdcall MsiGetSourcePathA(long str ptr ptr) MsiGetSourcePathA
76 stdcall MsiGetSourcePathW(long wstr ptr ptr) MsiGetSourcePathW
77 stdcall MsiGetSummaryInformationA(long str long ptr) MsiGetSummaryInformationA
78 stdcall MsiGetSummaryInformationW(long wstr long ptr) MsiGetSummaryInformationW
79 stdcall MsiGetTargetPathA(long str ptr ptr) MsiGetTargetPathA
80 stdcall MsiGetTargetPathW(long wstr ptr ptr) MsiGetTargetPathW
81 stdcall MsiGetUserInfoA(str ptr ptr ptr ptr ptr ptr) MsiGetUserInfoA
82 stdcall MsiGetUserInfoW(wstr ptr ptr ptr ptr ptr ptr) MsiGetUserInfoW
83 stub MsiInstallMissingComponentA
84 stub MsiInstallMissingComponentW
85 stub MsiInstallMissingFileA
86 stub MsiInstallMissingFileW
87 stdcall MsiInstallProductA(str str) MsiInstallProductA
88 stdcall MsiInstallProductW(wstr wstr) MsiInstallProductW
89 stdcall MsiLocateComponentA(str ptr long) MsiLocateComponentA
90 stdcall MsiLocateComponentW(wstr ptr long) MsiLocateComponentW
91 stdcall MsiOpenDatabaseA(str str ptr) MsiOpenDatabaseA
92 stdcall MsiOpenDatabaseW(wstr wstr ptr) MsiOpenDatabaseW
93 stdcall MsiOpenPackageA(str ptr) MsiOpenPackageA
94 stdcall MsiOpenPackageW(wstr ptr) MsiOpenPackageW
95 stdcall MsiOpenProductA(str ptr) MsiOpenProductA
96 stdcall MsiOpenProductW(wstr ptr) MsiOpenProductW
97 stdcall MsiPreviewBillboardA(long str str) MsiPreviewBillboardA
98 stdcall MsiPreviewBillboardW(long wstr wstr) MsiPreviewBillboardW
99 stdcall MsiPreviewDialogA(long str) MsiPreviewDialogA
100 stdcall MsiPreviewDialogW(long wstr) MsiPreviewDialogW
101 stub MsiProcessAdvertiseScriptA
102 stub MsiProcessAdvertiseScriptW
103 stdcall MsiProcessMessage(long long long) MsiProcessMessage
104 stub MsiProvideComponentA
105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr) MsiProvideComponentFromDescriptorA
106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr) MsiProvideComponentFromDescriptorW
107 stub MsiProvideComponentW
108 stdcall MsiProvideQualifiedComponentA(str str long ptr ptr) MsiProvideQualifiedComponentA
109 stdcall MsiProvideQualifiedComponentW(wstr wstr long ptr ptr) MsiProvideQualifiedComponentW
110 stdcall MsiQueryFeatureStateA(str str) MsiQueryFeatureStateA
111 stdcall MsiQueryFeatureStateW(wstr wstr) MsiQueryFeatureStateW
112 stdcall MsiQueryProductStateA(str) MsiQueryProductStateA
113 stdcall MsiQueryProductStateW(wstr) MsiQueryProductStateW
114 stdcall MsiRecordDataSize(long long) MsiRecordDataSize
115 stdcall MsiRecordGetFieldCount(long) MsiRecordGetFieldCount
116 stdcall MsiRecordGetInteger(long long) MsiRecordGetInteger
117 stdcall MsiRecordGetStringA(long long ptr ptr) MsiRecordGetStringA
118 stdcall MsiRecordGetStringW(long long ptr ptr) MsiRecordGetStringW
119 stdcall MsiRecordIsNull(long long) MsiRecordIsNull
120 stdcall MsiRecordReadStream(long long ptr ptr) MsiRecordReadStream
121 stdcall MsiRecordSetInteger(long long long) MsiRecordSetInteger
122 stdcall MsiRecordSetStreamA(long long str) MsiRecordSetStreamA
123 stdcall MsiRecordSetStreamW(long long wstr) MsiRecordSetStreamW
124 stdcall MsiRecordSetStringA(long long str) MsiRecordSetStringA
125 stdcall MsiRecordSetStringW(long long wstr) MsiRecordSetStringW
126 stdcall MsiReinstallFeatureA(str str long) MsiReinstallFeatureA
127 stub MsiReinstallFeatureFromDescriptorA
128 stub MsiReinstallFeatureFromDescriptorW
129 stdcall MsiReinstallFeatureW(wstr wstr long) MsiReinstallFeatureW
130 stdcall MsiReinstallProductA(str long) MsiReinstallProductA
131 stdcall MsiReinstallProductW(wstr long) MsiReinstallProductW
132 stdcall MsiSequenceA(long str long) MsiSequenceA
133 stdcall MsiSequenceW(long wstr long) MsiSequenceW
134 stdcall MsiSetComponentStateA(long str long) MsiSetComponentStateA
135 stdcall MsiSetComponentStateW(long wstr long) MsiSetComponentStateW
136 stdcall MsiSetExternalUIA(ptr long ptr) MsiSetExternalUIA
137 stdcall MsiSetExternalUIW(ptr long ptr) MsiSetExternalUIW
138 stdcall MsiSetFeatureStateA(long str long) MsiSetFeatureStateA
139 stdcall MsiSetFeatureStateW(long wstr long) MsiSetFeatureStateW
140 stdcall MsiSetInstallLevel(long long) MsiSetInstallLevel
141 stdcall MsiSetInternalUI(long ptr) MsiSetInternalUI
142 stub MsiVerifyDiskSpace
143 stdcall MsiSetMode(long long long) MsiSetMode
144 stdcall MsiSetPropertyA(long str str) MsiSetPropertyA
145 stdcall MsiSetPropertyW(long wstr wstr) MsiSetPropertyW
146 stdcall MsiSetTargetPathA(long str str) MsiSetTargetPathA
147 stdcall MsiSetTargetPathW(long wstr wstr) MsiSetTargetPathW
148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyA
149 stdcall MsiSummaryInfoGetPropertyCount(long ptr) MsiSummaryInfoGetPropertyCount
150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyW
151 stdcall MsiSummaryInfoPersist(long) MsiSummaryInfoPersist
152 stdcall MsiSummaryInfoSetPropertyA(long long long long ptr str) MsiSummaryInfoSetPropertyA
153 stdcall MsiSummaryInfoSetPropertyW(long long long long ptr wstr) MsiSummaryInfoSetPropertyW
154 stdcall MsiUseFeatureA(str str) MsiUseFeatureA
155 stdcall MsiUseFeatureW(wstr wstr) MsiUseFeatureW
156 stdcall MsiVerifyPackageA(str) MsiVerifyPackageA
157 stdcall MsiVerifyPackageW(wstr) MsiVerifyPackageW
158 stdcall MsiViewClose(long) MsiViewClose
159 stdcall MsiViewExecute(long long) MsiViewExecute
160 stdcall MsiViewFetch(long ptr) MsiViewFetch
161 stdcall MsiViewGetErrorA(long ptr ptr) MsiViewGetErrorA
162 stdcall MsiViewGetErrorW(long ptr ptr) MsiViewGetErrorW
163 stdcall MsiViewModify(long long long) MsiViewModify
164 stdcall MsiDatabaseIsTablePersistentA(long str) MsiDatabaseIsTablePersistentA
165 stdcall MsiDatabaseIsTablePersistentW(long wstr) MsiDatabaseIsTablePersistentW
166 stdcall MsiViewGetColumnInfo(long long ptr) MsiViewGetColumnInfo
167 stdcall MsiRecordClearData(long) MsiRecordClearData
168 stdcall MsiEnableLogA(long str long) MsiEnableLogA
169 stdcall MsiEnableLogW(long wstr long) MsiEnableLogW
170 stdcall MsiFormatRecordA(long long ptr ptr) MsiFormatRecordA
171 stdcall MsiFormatRecordW(long long ptr ptr) MsiFormatRecordW
172 stdcall MsiGetComponentPathA(str str ptr ptr) MsiGetComponentPathA
173 stdcall MsiGetComponentPathW(wstr wstr ptr ptr) MsiGetComponentPathW
174 stdcall MsiApplyPatchA(str str long str) MsiApplyPatchA
175 stdcall MsiApplyPatchW(wstr wstr long wstr) MsiApplyPatchW
176 stdcall MsiAdvertiseScriptA(str long ptr long) MsiAdvertiseScriptA
177 stdcall MsiAdvertiseScriptW(wstr long ptr long) MsiAdvertiseScriptW
178 stub MsiGetPatchInfoA
179 stub MsiGetPatchInfoW
180 stdcall MsiEnumPatchesA(str long ptr ptr ptr) MsiEnumPatchesA
181 stdcall MsiEnumPatchesW(str long ptr ptr ptr) MsiEnumPatchesW
182 stdcall DllGetVersion(ptr) DllGetVersion
183 stub MsiGetProductCodeFromPackageCodeA
184 stub MsiGetProductCodeFromPackageCodeW
185 stub MsiCreateTransformSummaryInfoA
186 stub MsiCreateTransformSummaryInfoW
187 stub MsiQueryFeatureStateFromDescriptorA
188 stub MsiQueryFeatureStateFromDescriptorW
189 stdcall MsiConfigureProductExA(str long long str) MsiConfigureProductExA
190 stdcall MsiConfigureProductExW(wstr long long wstr) MsiConfigureProductExW
191 stub MsiInvalidateFeatureCache
192 stdcall MsiUseFeatureExA(str str long long) MsiUseFeatureExA
193 stdcall MsiUseFeatureExW(wstr wstr long long) MsiUseFeatureExW
194 stdcall MsiGetFileVersionA(str str ptr str ptr) MsiGetFileVersionA
195 stdcall MsiGetFileVersionW(wstr wstr ptr wstr ptr) MsiGetFileVersionW
196 stdcall MsiLoadStringA(long long long long long) MsiLoadStringA
197 stdcall MsiLoadStringW(long long long long long) MsiLoadStringW
198 stdcall MsiMessageBoxA(long long long long long long) MsiMessageBoxA
199 stdcall MsiMessageBoxW(long long long long long long) MsiMessageBoxW
200 stdcall MsiDecomposeDescriptorA(str ptr ptr ptr ptr) MsiDecomposeDescriptorA
201 stdcall MsiDecomposeDescriptorW(wstr ptr ptr ptr ptr) MsiDecomposeDescriptorW
202 stdcall MsiProvideQualifiedComponentExA(str str long str long long ptr ptr) MsiProvideQualifiedComponentExA
203 stdcall MsiProvideQualifiedComponentExW(wstr wstr long wstr long long ptr ptr) MsiProvideQualifiedComponentExW
204 stdcall MsiEnumRelatedProductsA(str long long ptr) MsiEnumRelatedProductsA
205 stdcall MsiEnumRelatedProductsW(wstr long long ptr) MsiEnumRelatedProductsW
206 stub MsiSetFeatureAttributesA
207 stub MsiSetFeatureAttributesW
208 stdcall MsiSourceListClearAllA(str str long) MsiSourceListClearAllA
209 stdcall MsiSourceListClearAllW(wstr wstr long) MsiSourceListClearAllW
210 stdcall MsiSourceListAddSourceA(str str long str) MsiSourceListAddSourceA
211 stdcall MsiSourceListAddSourceW(wstr wstr long wstr) MsiSourceListAddSourceW
212 stub MsiSourceListForceResolutionA
213 stub MsiSourceListForceResolutionW
214 stub MsiIsProductElevatedA
215 stub MsiIsProductElevatedW
216 stdcall MsiGetShortcutTargetA(str ptr ptr ptr) MsiGetShortcutTargetA
217 stdcall MsiGetShortcutTargetW(wstr ptr ptr ptr) MsiGetShortcutTargetW
218 stdcall MsiGetFileHashA(str long ptr) MsiGetFileHashA
219 stdcall MsiGetFileHashW(wstr long ptr) MsiGetFileHashW
220 stub MsiEnumComponentCostsA
221 stub MsiEnumComponentCostsW
222 stdcall MsiCreateAndVerifyInstallerDirectory(long) MsiCreateAndVerifyInstallerDirectory
223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr) MsiGetFileSignatureInformationA
224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr) MsiGetFileSignatureInformationW
225 stdcall MsiProvideAssemblyA(str str long long str ptr) MsiProvideAssemblyA
226 stdcall MsiProvideAssemblyW(wstr wstr long long wstr ptr) MsiProvideAssemblyW
227 stdcall MsiAdvertiseProductExA(str str str long long long) MsiAdvertiseProductExA
228 stdcall MsiAdvertiseProductExW(wstr wstr wstr long long long) MsiAdvertiseProductExW
229 stub MsiNotifySidChangeA
230 stub MsiNotifySidChangeW
231 stdcall MsiOpenPackageExA(str long ptr) MsiOpenPackageExA
232 stdcall MsiOpenPackageExW(wstr long ptr) MsiOpenPackageExW
233 stub MsiDeleteUserDataA
234 stub MsiDeleteUserDataW
235 stub Migrate10CachedPackagesA
236 stub Migrate10CachedPackagesW
237 stub MsiRemovePatchesA
238 stub MsiRemovePatchesW
239 stub MsiApplyMultiplePatchesA
240 stub MsiApplyMultiplePatchesW
241 stub MsiExtractPatchXMLDataA
242 stub MsiExtractPatchXMLDataW
243 stub MsiGetPatchInfoExA
244 stub MsiGetPatchInfoExW
245 stdcall MsiEnumProductsExA(str str long long ptr ptr ptr ptr) MsiEnumProductsExA
246 stdcall MsiEnumProductsExW(wstr wstr long long ptr ptr ptr ptr) MsiEnumProductsExW
247 stub MsiGetProductInfoExA
248 stub MsiGetProductInfoExW
249 stdcall MsiQueryComponentStateA(str str long str ptr) MsiQueryComponentStateA
250 stub MsiQueryComponentStateW
251 stub MsiQueryFeatureStateExA
252 stub MsiQueryFeatureStateExW
253 stub MsiDeterminePatchSequenceA
254 stub MsiDeterminePatchSequenceW
255 stub MsiSourceListAddSourceExA
256 stub MsiSourceListAddSourceExW
257 stub MsiSourceListClearSourceA
258 stub MsiSourceListClearSourceW
259 stub MsiSourceListClearAllExA
260 stub MsiSourceListClearAllExW
261 stub MsiSourceListForceResolutionExA
262 stub MsiSourceListForceResolutionExW
263 stub MsiSourceListEnumSourcesA
264 stub MsiSourceListEnumSourcesW
265 stdcall MsiSourceListGetInfoA(str str long long str ptr ptr) MsiSourceListGetInfoA
266 stub MsiSourceListGetInfoW
267 stub MsiSourceListSetInfoA
268 stub MsiSourceListSetInfoW
269 stub MsiEnumPatchesExA
270 stub MsiEnumPatchesExW
271 stub MsiSourceListEnumMediaDisksA
272 stub MsiSourceListEnumMediaDisksW
273 stub MsiSourceListAddMediaDiskA
274 stub MsiSourceListAddMediaDiskW
275 stub MsiSourceListClearMediaDiskA
276 stub MsiSourceListClearMediaDiskW
277 stub MsiDetermineApplicablePatchesA
278 stub MsiDetermineApplicablePatchesW
279 stub MsiMessageBoxExA
280 stub MsiMessageBoxExW
281 stub MsiSetExternalUIRecord
@ stdcall DllCanUnloadNow() DllCanUnloadNow
@ stdcall DllGetClassObject(ptr ptr ptr) DllGetClassObject
@ stdcall DllRegisterServer() DllRegisterServer
@ stdcall DllUnregisterServer() DllUnregisterServer
--- NEW FILE: msipriv.h ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2005 Mike McCormack for CodeWeavers
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __WINE_MSI_PRIVATE__
#define __WINE_MSI_PRIVATE__
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "oaidl.h"
#include "winnls.h"
#include "wine-lgpl/list.h"
#define MSI_DATASIZEMASK 0x00ff
#define MSITYPE_VALID 0x0100
#define MSITYPE_LOCALIZABLE 0x200
#define MSITYPE_STRING 0x0800
#define MSITYPE_NULLABLE 0x1000
#define MSITYPE_KEY 0x2000
#define MSITYPE_TEMPORARY 0x4000
/* Word Count masks */
#define MSIWORDCOUNT_SHORTFILENAMES 0x0001
#define MSIWORDCOUNT_COMPRESSED 0x0002
#define MSIWORDCOUNT_ADMINISTRATIVE 0x0004
#define MSIWORDCOUNT_PRIVILEGES 0x0008
/* Install UI level mask for AND operation to exclude flags */
#define INSTALLUILEVEL_MASK 0x0007
#define MSITYPE_IS_BINARY(type) (((type) & ~MSITYPE_NULLABLE) == (MSITYPE_STRING|MSITYPE_VALID))
struct tagMSITABLE;
typedef struct tagMSITABLE MSITABLE;
struct string_table;
typedef struct string_table string_table;
struct tagMSIOBJECTHDR;
typedef struct tagMSIOBJECTHDR MSIOBJECTHDR;
typedef VOID (*msihandledestructor)( MSIOBJECTHDR * );
struct tagMSIOBJECTHDR
{
UINT magic;
UINT type;
LONG refcount;
msihandledestructor destructor;
};
typedef struct tagMSIDATABASE
{
MSIOBJECTHDR hdr;
IStorage *storage;
string_table *strings;
UINT bytes_per_strref;
LPWSTR path;
LPWSTR deletefile;
LPCWSTR mode;
struct list tables;
struct list transforms;
} MSIDATABASE;
typedef struct tagMSIVIEW MSIVIEW;
typedef struct tagMSIQUERY
{
MSIOBJECTHDR hdr;
MSIVIEW *view;
UINT row;
MSIDATABASE *db;
struct list mem;
} MSIQUERY;
/* maybe we can use a Variant instead of doing it ourselves? */
typedef struct tagMSIFIELD
{
UINT type;
union
{
INT iVal;
LPWSTR szwVal;
IStream *stream;
} u;
} MSIFIELD;
typedef struct tagMSIRECORD
{
MSIOBJECTHDR hdr;
UINT count; /* as passed to MsiCreateRecord */
MSIFIELD fields[1]; /* nb. array size is count+1 */
} MSIRECORD;
typedef struct tagMSISOURCELISTINFO
{
struct list entry;
DWORD context;
DWORD options;
LPCWSTR property;
LPWSTR value;
} MSISOURCELISTINFO;
typedef struct tagMSIMEDIADISK
{
struct list entry;
DWORD context;
DWORD options;
DWORD disk_id;
LPWSTR volume_label;
LPWSTR disk_prompt;
} MSIMEDIADISK;
typedef const struct tagMSICOLUMNHASHENTRY *MSIITERHANDLE;
typedef struct tagMSIVIEWOPS
{
/*
* fetch_int - reads one integer from {row,col} in the table
*
* This function should be called after the execute method.
* Data returned by the function should not change until
* close or delete is called.
* To get a string value, query the database's string table with
* the integer value returned from this function.
*/
UINT (*fetch_int)( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val );
/*
* fetch_stream - gets a stream from {row,col} in the table
*
* This function is similar to fetch_int, except fetches a
* stream instead of an integer.
*/
UINT (*fetch_stream)( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm );
/*
* set_row - sets values in a row as specified by mask
*
* Similar semantics to fetch_int
*/
UINT (*set_row)( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask );
/*
* Inserts a new row into the database from the records contents
*/
UINT (*insert_row)( struct tagMSIVIEW *view, MSIRECORD *record, BOOL temporary );
/*
* Deletes a row from the database
*/
UINT (*delete_row)( struct tagMSIVIEW *view, UINT row );
/*
* execute - loads the underlying data into memory so it can be read
*/
UINT (*execute)( struct tagMSIVIEW *view, MSIRECORD *record );
/*
* close - clears the data read by execute from memory
*/
UINT (*close)( struct tagMSIVIEW *view );
/*
* get_dimensions - returns the number of rows or columns in a table.
*
* The number of rows can only be queried after the execute method
* is called. The number of columns can be queried at any time.
*/
UINT (*get_dimensions)( struct tagMSIVIEW *view, UINT *rows, UINT *cols );
/*
* get_column_info - returns the name and type of a specific column
*
* The name is HeapAlloc'ed by this function and should be freed by
* the caller.
* The column information can be queried at any time.
*/
UINT (*get_column_info)( struct tagMSIVIEW *view, UINT n, LPWSTR *name, UINT *type );
/*
* modify - not yet implemented properly
*/
UINT (*modify)( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *record );
/*
* delete - destroys the structure completely
*/
UINT (*delete)( struct tagMSIVIEW * );
/*
* find_matching_rows - iterates through rows that match a value
*
* If the column type is a string then a string ID should be passed in.
* If the value to be looked up is an integer then no transformation of
* the input value is required, except if the column is a string, in which
* case a string ID should be passed in.
* The handle is an input/output parameter that keeps track of the current
* position in the iteration. It must be initialised to zero before the
* first call and continued to be passed in to subsequent calls.
*/
UINT (*find_matching_rows)( struct tagMSIVIEW *view, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle );
/*
* add_ref - increases the reference count of the table
*/
UINT (*add_ref)( struct tagMSIVIEW *view );
/*
* release - decreases the reference count of the table
*/
UINT (*release)( struct tagMSIVIEW *view );
/*
* add_column - adds a column to the table
*/
UINT (*add_column)( struct tagMSIVIEW *view, LPCWSTR table, UINT number, LPCWSTR column, UINT type, BOOL hold );
/*
* remove_column - removes the column represented by table name and column number from the table
*/
UINT (*remove_column)( struct tagMSIVIEW *view, LPCWSTR table, UINT number );
} MSIVIEWOPS;
struct tagMSIVIEW
{
MSIOBJECTHDR hdr;
const MSIVIEWOPS *ops;
};
struct msi_dialog_tag;
typedef struct msi_dialog_tag msi_dialog;
typedef struct tagMSIPACKAGE
{
MSIOBJECTHDR hdr;
MSIDATABASE *db;
struct list components;
struct list features;
struct list files;
struct list tempfiles;
struct list folders;
LPWSTR ActionFormat;
LPWSTR LastAction;
struct list classes;
struct list extensions;
struct list progids;
struct list mimes;
struct list appids;
struct tagMSISCRIPT *script;
struct list RunningActions;
LPWSTR BaseURL;
LPWSTR PackagePath;
LPWSTR ProductCode;
UINT CurrentInstallState;
msi_dialog *dialog;
LPWSTR next_dialog;
float center_x;
float center_y;
UINT WordCount;
struct list subscriptions;
struct list sourcelist_info;
struct list sourcelist_media;
unsigned char scheduled_action_running : 1;
unsigned char commit_action_running : 1;
unsigned char rollback_action_running : 1;
} MSIPACKAGE;
typedef struct tagMSIPREVIEW
{
MSIOBJECTHDR hdr;
MSIPACKAGE *package;
msi_dialog *dialog;
} MSIPREVIEW;
#define MSI_MAX_PROPS 20
typedef struct tagMSISUMMARYINFO
{
MSIOBJECTHDR hdr;
IStorage *storage;
DWORD update_count;
PROPVARIANT property[MSI_MAX_PROPS];
} MSISUMMARYINFO;
typedef struct tagMSIFEATURE
{
struct list entry;
LPWSTR Feature;
LPWSTR Feature_Parent;
LPWSTR Title;
LPWSTR Description;
INT Display;
INT Level;
LPWSTR Directory;
INT Attributes;
INSTALLSTATE Installed;
INSTALLSTATE ActionRequest;
INSTALLSTATE Action;
struct list Children;
struct list Components;
INT Cost;
} MSIFEATURE;
typedef struct tagMSICOMPONENT
{
struct list entry;
DWORD magic;
LPWSTR Component;
LPWSTR ComponentId;
LPWSTR Directory;
INT Attributes;
LPWSTR Condition;
LPWSTR KeyPath;
INSTALLSTATE Installed;
INSTALLSTATE ActionRequest;
INSTALLSTATE Action;
BOOL ForceLocalState;
BOOL Enabled;
INT Cost;
INT RefCount;
LPWSTR FullKeypath;
LPWSTR AdvertiseString;
unsigned int hasAdvertiseFeature:1;
unsigned int hasLocalFeature:1;
unsigned int hasSourceFeature:1;
} MSICOMPONENT;
typedef struct tagComponentList
{
struct list entry;
MSICOMPONENT *component;
} ComponentList;
typedef struct tagFeatureList
{
struct list entry;
MSIFEATURE *feature;
} FeatureList;
typedef struct tagMSIFOLDER
{
struct list entry;
LPWSTR Directory;
LPWSTR Parent;
LPWSTR TargetDefault;
LPWSTR SourceLongPath;
LPWSTR SourceShortPath;
LPWSTR ResolvedTarget;
LPWSTR ResolvedSource;
LPWSTR Property; /* initially set property */
INT State;
/* 0 = uninitialized */
/* 1 = existing */
/* 2 = created remove if empty */
/* 3 = created persist if empty */
INT Cost;
INT Space;
} MSIFOLDER;
typedef enum _msi_file_state {
msifs_invalid,
msifs_missing,
msifs_overwrite,
msifs_present,
msifs_installed,
msifs_skipped,
} msi_file_state;
typedef struct tagMSIFILE
{
struct list entry;
LPWSTR File;
MSICOMPONENT *Component;
LPWSTR FileName;
LPWSTR ShortName;
LPWSTR LongName;
INT FileSize;
LPWSTR Version;
LPWSTR Language;
INT Attributes;
INT Sequence;
msi_file_state state;
LPWSTR SourcePath;
LPWSTR TargetPath;
BOOL IsCompressed;
} MSIFILE;
typedef struct tagMSITEMPFILE
{
struct list entry;
LPWSTR Path;
} MSITEMPFILE;
typedef struct tagMSIAPPID
{
struct list entry;
LPWSTR AppID; /* Primary key */
LPWSTR RemoteServerName;
LPWSTR LocalServer;
LPWSTR ServiceParameters;
LPWSTR DllSurrogate;
BOOL ActivateAtStorage;
BOOL RunAsInteractiveUser;
} MSIAPPID;
typedef struct tagMSIPROGID MSIPROGID;
typedef struct tagMSICLASS
{
struct list entry;
LPWSTR clsid; /* Primary Key */
LPWSTR Context; /* Primary Key */
MSICOMPONENT *Component;
MSIPROGID *ProgID;
LPWSTR ProgIDText;
LPWSTR Description;
MSIAPPID *AppID;
LPWSTR FileTypeMask;
LPWSTR IconPath;
LPWSTR DefInprocHandler;
LPWSTR DefInprocHandler32;
LPWSTR Argument;
MSIFEATURE *Feature;
INT Attributes;
/* not in the table, set during installation */
BOOL Installed;
} MSICLASS;
typedef struct tagMSIMIME MSIMIME;
typedef struct tagMSIEXTENSION
{
struct list entry;
LPWSTR Extension; /* Primary Key */
MSICOMPONENT *Component;
MSIPROGID *ProgID;
LPWSTR ProgIDText;
MSIMIME *Mime;
MSIFEATURE *Feature;
/* not in the table, set during installation */
BOOL Installed;
struct list verbs;
} MSIEXTENSION;
struct tagMSIPROGID
{
struct list entry;
LPWSTR ProgID; /* Primary Key */
MSIPROGID *Parent;
MSICLASS *Class;
LPWSTR Description;
LPWSTR IconPath;
/* not in the table, set during installation */
BOOL InstallMe;
MSIPROGID *CurVer;
MSIPROGID *VersionInd;
};
typedef struct tagMSIVERB
{
struct list entry;
LPWSTR Verb;
INT Sequence;
LPWSTR Command;
LPWSTR Argument;
} MSIVERB;
struct tagMSIMIME
{
struct list entry;
LPWSTR ContentType; /* Primary Key */
MSIEXTENSION *Extension;
LPWSTR clsid;
MSICLASS *Class;
/* not in the table, set during installation */
BOOL InstallMe;
};
enum SCRIPTS {
INSTALL_SCRIPT = 0,
COMMIT_SCRIPT = 1,
ROLLBACK_SCRIPT = 2,
TOTAL_SCRIPTS = 3
};
#define SEQUENCE_UI 0x1
#define SEQUENCE_EXEC 0x2
#define SEQUENCE_INSTALL 0x10
typedef struct tagMSISCRIPT
{
LPWSTR *Actions[TOTAL_SCRIPTS];
UINT ActionCount[TOTAL_SCRIPTS];
BOOL ExecuteSequenceRun;
BOOL CurrentlyScripting;
UINT InWhatSequence;
LPWSTR *UniqueActions;
UINT UniqueActionsCount;
} MSISCRIPT;
#define MSIHANDLETYPE_ANY 0
#define MSIHANDLETYPE_DATABASE 1
#define MSIHANDLETYPE_SUMMARYINFO 2
#define MSIHANDLETYPE_VIEW 3
#define MSIHANDLETYPE_RECORD 4
#define MSIHANDLETYPE_PACKAGE 5
#define MSIHANDLETYPE_PREVIEW 6
#define MSI_MAJORVERSION 3
#define MSI_MINORVERSION 1
#define MSI_BUILDNUMBER 4000
#define GUID_SIZE 39
#define MSIHANDLE_MAGIC 0x4d434923
DEFINE_GUID(CLSID_IMsiServer, 0x000C101C,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_IMsiServerX1, 0x000C103E,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_IMsiServerX2, 0x000C1090,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_IMsiServerX3, 0x000C1094,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_IMsiServerMessage, 0x000C101D,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_IWineMsiRemoteCustomAction,0xBA26E6FA,0x4F27,0x4f56,0x95,0x3A,0x3F,0x90,0x27,0x20,0x18,0xAA);
DEFINE_GUID(CLSID_IWineMsiRemotePackage,0x902b3592,0x9d08,0x4dfd,0xa5,0x93,0xd0,0x7c,0x52,0x54,0x64,0x21);
/* handle unicode/ascii output in the Msi* API functions */
typedef struct {
BOOL unicode;
union {
LPSTR a;
LPWSTR w;
} str;
} awstring;
typedef struct {
BOOL unicode;
union {
LPCSTR a;
LPCWSTR w;
} str;
} awcstring;
UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz );
/* msi server interface */
extern ITypeLib *get_msi_typelib( LPWSTR *path );
extern HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj );
extern HRESULT create_msi_remote_package( IUnknown *pOuter, LPVOID *ppObj );
extern IUnknown *msi_get_remote(MSIHANDLE handle);
/* handle functions */
extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * );
extern MSIHANDLE alloc_msi_remote_handle( IUnknown *unk );
extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy );
extern void msiobj_addref(MSIOBJECTHDR *);
extern int msiobj_release(MSIOBJECTHDR *);
extern void msiobj_lock(MSIOBJECTHDR *);
extern void msiobj_unlock(MSIOBJECTHDR *);
extern void msi_free_handle_table(void);
extern void free_cached_tables( MSIDATABASE *db );
extern void msi_free_transforms( MSIDATABASE *db );
extern UINT MSI_CommitTables( MSIDATABASE *db );
/* string table functions */
enum StringPersistence
{
StringPersistent = 0,
StringNonPersistent = 1
};
extern BOOL msi_addstringW( string_table *st, UINT string_no, const WCHAR *data, int len, UINT refcount, enum StringPersistence persistence );
extern UINT msi_id2stringW( const string_table *st, UINT string_no, LPWSTR buffer, UINT *sz );
extern UINT msi_id2stringA( const string_table *st, UINT string_no, LPSTR buffer, UINT *sz );
extern UINT msi_string2idW( const string_table *st, LPCWSTR buffer, UINT *id );
extern UINT msi_string2idA( const string_table *st, LPCSTR str, UINT *id );
extern VOID msi_destroy_stringtable( string_table *st );
extern UINT msi_strcmp( const string_table *st, UINT lval, UINT rval, UINT *res );
extern const WCHAR *msi_string_lookup_id( const string_table *st, UINT id );
extern HRESULT msi_init_string_table( IStorage *stg );
extern string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref );
extern UINT msi_save_string_table( const string_table *st, IStorage *storage );
extern void msi_table_set_strref(UINT bytes_per_strref);
extern BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name );
extern MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table );
extern UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
USHORT **pdata, UINT *psz );
extern UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
BYTE **pdata, UINT *psz );
extern UINT write_stream_data( IStorage *stg, LPCWSTR stname,
LPCVOID data, UINT sz, BOOL bTable );
/* transform functions */
extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg );
extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
LPCWSTR szTransformFile, int iErrorCond );
extern void append_storage_to_db( MSIDATABASE *db, IStorage *stg );
/* action internals */
extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR );
extern void ACTION_free_package_structures( MSIPACKAGE* );
extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR);
extern UINT ACTION_ForceReboot(MSIPACKAGE *package);
extern UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode );
extern UINT MSI_SetFeatureStates( MSIPACKAGE *package );
extern UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine );
/* record internals */
extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *);
extern UINT MSI_RecordGetIStream( MSIRECORD *, unsigned int, IStream **);
extern const WCHAR *MSI_RecordGetString( const MSIRECORD *, unsigned int );
extern MSIRECORD *MSI_CreateRecord( unsigned int );
extern UINT MSI_RecordSetInteger( MSIRECORD *, unsigned int, int );
extern UINT MSI_RecordSetStringW( MSIRECORD *, unsigned int, LPCWSTR );
extern UINT MSI_RecordSetStringA( MSIRECORD *, unsigned int, LPCSTR );
extern BOOL MSI_RecordIsNull( MSIRECORD *, unsigned int );
extern UINT MSI_RecordGetStringW( MSIRECORD * , unsigned int, LPWSTR, DWORD *);
extern UINT MSI_RecordGetStringA( MSIRECORD *, unsigned int, LPSTR, DWORD *);
extern int MSI_RecordGetInteger( MSIRECORD *, unsigned int );
extern UINT MSI_RecordReadStream( MSIRECORD *, unsigned int, char *, DWORD *);
extern unsigned int MSI_RecordGetFieldCount( const MSIRECORD *rec );
extern UINT MSI_RecordSetStream( MSIRECORD *, unsigned int, IStream * );
extern UINT MSI_RecordDataSize( MSIRECORD *, unsigned int );
extern UINT MSI_RecordStreamToFile( MSIRECORD *, unsigned int, LPCWSTR );
extern UINT MSI_RecordCopyField( MSIRECORD *, unsigned int, MSIRECORD *, unsigned int );
/* stream internals */
extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm );
extern UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm );
extern void enum_stream_names( IStorage *stg );
extern BOOL decode_streamname(LPCWSTR in, LPWSTR out);
extern LPWSTR encode_streamname(BOOL bTable, LPCWSTR in);
/* database internals */
extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** );
extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** );
extern UINT MSI_OpenQuery( MSIDATABASE *, MSIQUERY **, LPCWSTR, ... );
typedef UINT (*record_func)( MSIRECORD *, LPVOID );
extern UINT MSI_IterateRecords( MSIQUERY *, DWORD *, record_func, LPVOID );
extern MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR query, ... );
extern UINT MSI_DatabaseImport( MSIDATABASE *, LPCWSTR, LPCWSTR );
extern UINT MSI_DatabaseExport( MSIDATABASE *, LPCWSTR, LPCWSTR, LPCWSTR );
extern UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *, LPCWSTR, MSIRECORD ** );
/* view internals */
extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * );
extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** );
extern UINT MSI_ViewClose( MSIQUERY* );
extern UINT MSI_ViewGetColumnInfo(MSIQUERY *, MSICOLINFO, MSIRECORD **);
extern UINT MSI_ViewModify( MSIQUERY *, MSIMODIFY, MSIRECORD * );
extern UINT VIEW_find_column( MSIVIEW *, LPCWSTR, UINT * );
/* install internals */
extern UINT MSI_SetInstallLevel( MSIPACKAGE *package, int iInstallLevel );
/* package internals */
extern MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *, LPCWSTR );
extern UINT MSI_OpenPackageW( LPCWSTR szPackage, MSIPACKAGE **pPackage );
extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD * );
extern UINT MSI_GetPropertyW( MSIPACKAGE *, LPCWSTR, LPWSTR, DWORD * );
extern UINT MSI_GetPropertyA(MSIPACKAGE *, LPCSTR, LPSTR, DWORD* );
extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR );
extern UINT MSI_GetComponentStateW( MSIPACKAGE *, LPCWSTR, INSTALLSTATE *, INSTALLSTATE * );
extern UINT MSI_GetFeatureStateW( MSIPACKAGE *, LPCWSTR, INSTALLSTATE *, INSTALLSTATE * );
extern UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE*, LPCWSTR, INSTALLSTATE );
extern LPCWSTR msi_download_file( LPCWSTR szUrl, LPWSTR filename );
extern UINT msi_package_add_info(MSIPACKAGE *, DWORD, DWORD, LPCWSTR, LPWSTR);
extern UINT msi_package_add_media_disk(MSIPACKAGE *, DWORD, DWORD, DWORD, LPWSTR, LPWSTR);
extern UINT msi_clone_properties(MSIPACKAGE *);
/* for deformating */
extern UINT MSI_FormatRecordW( MSIPACKAGE *, MSIRECORD *, LPWSTR, DWORD * );
/* registry data encoding/decoding functions */
extern BOOL unsquash_guid(LPCWSTR in, LPWSTR out);
extern BOOL squash_guid(LPCWSTR in, LPWSTR out);
extern BOOL encode_base85_guid(GUID *,LPWSTR);
extern BOOL decode_base85_guid(LPCWSTR,GUID*);
extern UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create);
extern UINT MSIREG_OpenFeatures(HKEY* key);
extern UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserDataFeaturesKey(LPCWSTR szProduct, HKEY *key, BOOL create);
extern UINT MSIREG_OpenComponents(HKEY* key);
extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
extern UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserDataComponentKey(LPCWSTR szComponent, HKEY *key, BOOL create);
extern UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenPatchesKey(LPCWSTR szPatch, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserDataProductKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenInstallPropertiesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
extern UINT MSIREG_DeleteProductKey(LPCWSTR szProduct);
extern UINT MSIREG_DeleteUserProductKey(LPCWSTR szProduct);
extern UINT MSIREG_DeleteUserDataProductKey(LPCWSTR szProduct);
extern LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name );
extern BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val);
extern DWORD msi_version_str_to_dword(LPCWSTR p);
extern LPWSTR msi_version_dword_to_str(DWORD version);
extern LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value );
extern LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value );
extern LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val );
extern LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val );
/* msi dialog interface */
typedef UINT (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, msi_dialog* );
extern msi_dialog *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog*, msi_dialog_event_handler );
extern UINT msi_dialog_run_message_loop( msi_dialog* );
extern void msi_dialog_end_dialog( msi_dialog* );
extern void msi_dialog_check_messages( HANDLE );
extern void msi_dialog_do_preview( msi_dialog* );
extern void msi_dialog_destroy( msi_dialog* );
extern BOOL msi_dialog_register_class( void );
extern void msi_dialog_unregister_class( void );
extern void msi_dialog_handle_event( msi_dialog*, LPCWSTR, LPCWSTR, MSIRECORD * );
extern UINT msi_dialog_reset( msi_dialog *dialog );
extern UINT msi_dialog_directorylist_up( msi_dialog *dialog );
extern msi_dialog *msi_dialog_get_parent( msi_dialog *dialog );
extern LPWSTR msi_dialog_get_name( msi_dialog *dialog );
extern UINT msi_spawn_error_dialog( MSIPACKAGE*, LPWSTR, LPWSTR );
/* preview */
extern MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE * );
extern UINT MSI_PreviewDialogW( MSIPREVIEW *, LPCWSTR );
/* summary information */
extern MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount );
extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty );
extern LPWSTR msi_get_suminfo_product( IStorage *stg );
/* undocumented functions */
UINT WINAPI MsiCreateAndVerifyInstallerDirectory( DWORD );
UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR, LPWSTR, LPWSTR, LPWSTR, DWORD * );
UINT WINAPI MsiDecomposeDescriptorA( LPCSTR, LPSTR, LPSTR, LPSTR, DWORD * );
LANGID WINAPI MsiLoadStringW( MSIHANDLE, UINT, LPWSTR, int, LANGID );
LANGID WINAPI MsiLoadStringA( MSIHANDLE, UINT, LPSTR, int, LANGID );
/* UI globals */
extern INSTALLUILEVEL gUILevel;
extern HWND gUIhwnd;
extern INSTALLUI_HANDLERA gUIHandlerA;
extern INSTALLUI_HANDLERW gUIHandlerW;
extern DWORD gUIFilter;
extern LPVOID gUIContext;
extern WCHAR gszLogFile[MAX_PATH];
extern HINSTANCE msi_hInstance;
/* action related functions */
extern UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force);
extern UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script);
extern void ACTION_FinishCustomActions( const MSIPACKAGE* package);
extern UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action, UINT script, BOOL execute);
static inline void msi_feature_set_state( MSIFEATURE *feature, INSTALLSTATE state )
{
feature->ActionRequest = state;
feature->Action = state;
}
static inline void msi_component_set_state( MSICOMPONENT *comp, INSTALLSTATE state )
{
comp->ActionRequest = state;
comp->Action = state;
}
/* actions in other modules */
extern UINT ACTION_AppSearch(MSIPACKAGE *package);
extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package);
extern UINT ACTION_InstallFiles(MSIPACKAGE *package);
extern UINT ACTION_RemoveFiles(MSIPACKAGE *package);
extern UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
extern UINT ACTION_RegisterClassInfo(MSIPACKAGE *package);
extern UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package);
extern UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package);
extern UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package);
extern UINT ACTION_RegisterFonts(MSIPACKAGE *package);
/* Helpers */
extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data );
extern LPWSTR msi_dup_record_field(MSIRECORD *row, INT index);
extern LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop);
extern int msi_get_property_int( MSIPACKAGE *package, LPCWSTR prop, int def );
extern LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source,
BOOL set_prop, BOOL load_prop, MSIFOLDER **folder);
extern MSICOMPONENT *get_loaded_component( MSIPACKAGE* package, LPCWSTR Component );
extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature );
extern MSIFILE *get_loaded_file( MSIPACKAGE* package, LPCWSTR file );
extern MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir );
extern int track_tempfile(MSIPACKAGE *package, LPCWSTR path);
extern UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action);
extern void msi_free_action_script(MSIPACKAGE *package, UINT script);
extern LPWSTR build_icon_path(MSIPACKAGE *, LPCWSTR);
extern LPWSTR build_directory_name(DWORD , ...);
extern BOOL create_full_pathW(const WCHAR *path);
extern BOOL ACTION_VerifyComponentForAction(const MSICOMPONENT*, INSTALLSTATE);
extern BOOL ACTION_VerifyFeatureForAction(const MSIFEATURE*, INSTALLSTATE);
extern void reduce_to_longfilename(WCHAR*);
extern void reduce_to_shortfilename(WCHAR*);
extern LPWSTR create_component_advertise_string(MSIPACKAGE*, MSICOMPONENT*, LPCWSTR);
extern void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature);
extern UINT register_unique_action(MSIPACKAGE *, LPCWSTR);
extern BOOL check_unique_action(const MSIPACKAGE *, LPCWSTR);
extern WCHAR* generate_error_string(MSIPACKAGE *, UINT, DWORD, ... );
extern UINT msi_create_component_directories( MSIPACKAGE *package );
extern void msi_ui_error( DWORD msg_id, DWORD type );
/* control event stuff */
extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event,
MSIRECORD *data);
extern VOID ControlEvent_CleanupDialogSubscriptions(MSIPACKAGE *package, LPWSTR dialog);
extern VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package);
extern VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, msi_dialog *dialog,
LPCWSTR event, LPCWSTR control, LPCWSTR attribute);
extern VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event,
LPCWSTR control, LPCWSTR attribute );
/* OLE automation */
extern HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj);
extern HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch);
extern HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid);
/* Scripting */
extern DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action);
/* User Interface messages from the actions */
extern void ui_progress(MSIPACKAGE *, int, int, int, int);
extern void ui_actiondata(MSIPACKAGE *, LPCWSTR, MSIRECORD *);
/* string consts use a number of places and defined in helpers.c*/
extern const WCHAR cszSourceDir[];
extern const WCHAR cszSOURCEDIR[];
extern const WCHAR szProductCode[];
extern const WCHAR cszRootDrive[];
extern const WCHAR cszbs[];
/* memory allocation macro functions */
static inline void *msi_alloc( size_t len )
{
return HeapAlloc( GetProcessHeap(), 0, len );
}
static inline void *msi_alloc_zero( size_t len )
{
return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
}
static inline void *msi_realloc( void *mem, size_t len )
{
return HeapReAlloc( GetProcessHeap(), 0, mem, len );
}
static inline void *msi_realloc_zero( void *mem, size_t len )
{
return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len );
}
static inline BOOL msi_free( void *mem )
{
return HeapFree( GetProcessHeap(), 0, mem );
}
static inline char *strdupWtoA( LPCWSTR str )
{
LPSTR ret = NULL;
DWORD len;
if (!str) return ret;
len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
ret = msi_alloc( len );
if (ret)
WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
return ret;
}
static inline LPWSTR strdupAtoW( LPCSTR str )
{
LPWSTR ret = NULL;
DWORD len;
if (!str) return ret;
len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
ret = msi_alloc( len * sizeof(WCHAR) );
if (ret)
MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
return ret;
}
static inline LPWSTR strdupW( LPCWSTR src )
{
LPWSTR dest;
if (!src) return NULL;
dest = msi_alloc( (lstrlenW(src)+1)*sizeof(WCHAR) );
if (dest)
lstrcpyW(dest, src);
return dest;
}
#endif /* __WINE_MSI_PRIVATE__ */
--- NEW FILE: msiquery.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
#include "initguid.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static void MSI_CloseView( MSIOBJECTHDR *arg )
{
MSIQUERY *query = (MSIQUERY*) arg;
struct list *ptr, *t;
if( query->view && query->view->ops->delete )
query->view->ops->delete( query->view );
msiobj_release( &query->db->hdr );
LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
{
msi_free( ptr );
}
}
UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
{
LPWSTR col_name;
UINT i, count, r;
r = table->ops->get_dimensions( table, NULL, &count );
if( r != ERROR_SUCCESS )
return r;
for( i=1; i<=count; i++ )
{
INT x;
col_name = NULL;
r = table->ops->get_column_info( table, i, &col_name, NULL );
if( r != ERROR_SUCCESS )
return r;
x = lstrcmpW( name, col_name );
msi_free( col_name );
if( !x )
{
*n = i;
return ERROR_SUCCESS;
}
}
return ERROR_INVALID_PARAMETER;
}
UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
LPCSTR szQuery, MSIHANDLE *phView)
{
UINT r;
LPWSTR szwQuery;
TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
if( szQuery )
{
szwQuery = strdupAtoW( szQuery );
if( !szwQuery )
return ERROR_FUNCTION_FAILED;
}
else
szwQuery = NULL;
r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
msi_free( szwQuery );
return r;
}
UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
LPCWSTR szQuery, MSIQUERY **pView)
{
MSIQUERY *query;
UINT r;
TRACE("%s %p\n", debugstr_w(szQuery), pView);
if( !szQuery)
return ERROR_INVALID_PARAMETER;
/* pre allocate a handle to hold a pointer to the view */
query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
MSI_CloseView );
if( !query )
return ERROR_FUNCTION_FAILED;
msiobj_addref( &db->hdr );
query->row = 0;
query->db = db;
query->view = NULL;
list_init( &query->mem );
r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
if( r == ERROR_SUCCESS )
{
msiobj_addref( &query->hdr );
*pView = query;
}
msiobj_release( &query->hdr );
return r;
}
UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
{
UINT r;
int size = 100, res;
LPWSTR query;
/* construct the string */
for (;;)
{
va_list va;
query = msi_alloc( size*sizeof(WCHAR) );
va_start(va, fmt);
res = vsnprintfW(query, size, fmt, va);
va_end(va);
if (res == -1) size *= 2;
else if (res >= size) size = res + 1;
else break;
msi_free( query );
}
/* perform the query */
r = MSI_DatabaseOpenViewW(db, query, view);
msi_free(query);
return r;
}
UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
record_func func, LPVOID param )
{
MSIRECORD *rec = NULL;
UINT r, n = 0, max = 0;
r = MSI_ViewExecute( view, NULL );
if( r != ERROR_SUCCESS )
return r;
if( count )
max = *count;
/* iterate a query */
for( n = 0; (max == 0) || (n < max); n++ )
{
r = MSI_ViewFetch( view, &rec );
if( r != ERROR_SUCCESS )
break;
if (func)
r = func( rec, param );
msiobj_release( &rec->hdr );
if( r != ERROR_SUCCESS )
break;
}
MSI_ViewClose( view );
if( count )
*count = n;
if( r == ERROR_NO_MORE_ITEMS )
r = ERROR_SUCCESS;
return r;
}
/* return a single record from a query */
MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
{
MSIRECORD *rec = NULL;
MSIQUERY *view = NULL;
UINT r;
int size = 100, res;
LPWSTR query;
/* construct the string */
for (;;)
{
va_list va;
query = msi_alloc( size*sizeof(WCHAR) );
va_start(va, fmt);
res = vsnprintfW(query, size, fmt, va);
va_end(va);
if (res == -1) size *= 2;
else if (res >= size) size = res + 1;
else break;
msi_free( query );
}
/* perform the query */
r = MSI_DatabaseOpenViewW(db, query, &view);
msi_free(query);
if( r == ERROR_SUCCESS )
{
MSI_ViewExecute( view, NULL );
MSI_ViewFetch( view, &rec );
MSI_ViewClose( view );
msiobj_release( &view->hdr );
}
return rec;
}
UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
LPCWSTR szQuery, MSIHANDLE *phView)
{
MSIDATABASE *db;
MSIQUERY *query = NULL;
UINT ret;
TRACE("%s %p\n", debugstr_w(szQuery), phView);
db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
if( ret == ERROR_SUCCESS )
{
*phView = alloc_msihandle( &query->hdr );
if (! *phView)
ret = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &query->hdr );
}
msiobj_release( &db->hdr );
return ret;
}
UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
{
MSIVIEW *view;
MSIRECORD *rec;
UINT row_count = 0, col_count = 0, i, ival, ret, type;
TRACE("%p %p\n", query, prec );
view = query->view;
if( !view )
return ERROR_FUNCTION_FAILED;
ret = view->ops->get_dimensions( view, &row_count, &col_count );
if( ret )
return ret;
if( !col_count )
return ERROR_INVALID_PARAMETER;
if( query->row >= row_count )
return ERROR_NO_MORE_ITEMS;
rec = MSI_CreateRecord( col_count );
if( !rec )
return ERROR_FUNCTION_FAILED;
for( i=1; i<=col_count; i++ )
{
ret = view->ops->get_column_info( view, i, NULL, &type );
if( ret )
{
ERR("Error getting column type for %d\n", i );
continue;
}
if (!MSITYPE_IS_BINARY(type))
{
ret = view->ops->fetch_int( view, query->row, i, &ival );
if( ret )
{
ERR("Error fetching data for %d\n", i );
continue;
}
if( ! (type & MSITYPE_VALID ) )
ERR("Invalid type!\n");
/* check if it's nul (0) - if so, don't set anything */
if( !ival )
continue;
if( type & MSITYPE_STRING )
{
LPCWSTR sval;
sval = msi_string_lookup_id( query->db->strings, ival );
MSI_RecordSetStringW( rec, i, sval );
}
else
{
if( (type & MSI_DATASIZEMASK) == 2 )
MSI_RecordSetInteger( rec, i, ival - (1<<15) );
else
MSI_RecordSetInteger( rec, i, ival - (1<<31) );
}
}
else
{
IStream *stm = NULL;
ret = view->ops->fetch_stream( view, query->row, i, &stm );
if( ( ret == ERROR_SUCCESS ) && stm )
{
MSI_RecordSetIStream( rec, i, stm );
IStream_Release( stm );
}
else
ERR("failed to get stream\n");
}
}
query->row ++;
*prec = rec;
return ERROR_SUCCESS;
}
UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
{
MSIQUERY *query;
MSIRECORD *rec = NULL;
UINT ret;
TRACE("%ld %p\n", hView, record);
if( !record )
return ERROR_INVALID_PARAMETER;
*record = 0;
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
if( !query )
return ERROR_INVALID_HANDLE;
ret = MSI_ViewFetch( query, &rec );
if( ret == ERROR_SUCCESS )
{
*record = alloc_msihandle( &rec->hdr );
if (! *record)
ret = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &rec->hdr );
}
msiobj_release( &query->hdr );
return ret;
}
UINT MSI_ViewClose(MSIQUERY *query)
{
MSIVIEW *view;
TRACE("%p\n", query );
view = query->view;
if( !view )
return ERROR_FUNCTION_FAILED;
if( !view->ops->close )
return ERROR_FUNCTION_FAILED;
return view->ops->close( view );
}
UINT WINAPI MsiViewClose(MSIHANDLE hView)
{
MSIQUERY *query;
UINT ret;
TRACE("%ld\n", hView );
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
if( !query )
return ERROR_INVALID_HANDLE;
ret = MSI_ViewClose( query );
msiobj_release( &query->hdr );
return ret;
}
UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
{
MSIVIEW *view;
TRACE("%p %p\n", query, rec);
view = query->view;
if( !view )
return ERROR_FUNCTION_FAILED;
if( !view->ops->execute )
return ERROR_FUNCTION_FAILED;
query->row = 0;
return view->ops->execute( view, rec );
}
UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
{
MSIQUERY *query;
MSIRECORD *rec = NULL;
UINT ret;
TRACE("%ld %ld\n", hView, hRec);
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
if( !query )
return ERROR_INVALID_HANDLE;
if( hRec )
{
rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
if( !rec )
{
ret = ERROR_INVALID_HANDLE;
goto out;
}
}
msiobj_lock( &rec->hdr );
ret = MSI_ViewExecute( query, rec );
msiobj_unlock( &rec->hdr );
out:
msiobj_release( &query->hdr );
if( rec )
msiobj_release( &rec->hdr );
return ret;
}
static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
{
static const WCHAR fmt[] = { '%','d',0 };
WCHAR szType[0x10];
if (MSITYPE_IS_BINARY(type))
szType[0] = 'v';
else if (type & MSITYPE_LOCALIZABLE)
szType[0] = 'l';
else if (type & MSITYPE_STRING)
szType[0] = 's';
else
szType[0] = 'i';
if (type & MSITYPE_NULLABLE)
szType[0] &= ~0x20;
sprintfW( &szType[1], fmt, (type&0xff) );
TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
return MSI_RecordSetStringW( rec, field, szType );
}
UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
{
UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
MSIRECORD *rec;
MSIVIEW *view = query->view;
LPWSTR name;
if( !view )
return ERROR_FUNCTION_FAILED;
if( !view->ops->get_dimensions )
return ERROR_FUNCTION_FAILED;
r = view->ops->get_dimensions( view, NULL, &count );
if( r != ERROR_SUCCESS )
return r;
if( !count )
return ERROR_INVALID_PARAMETER;
rec = MSI_CreateRecord( count );
if( !rec )
return ERROR_FUNCTION_FAILED;
for( i=0; i<count; i++ )
{
name = NULL;
r = view->ops->get_column_info( view, i+1, &name, &type );
if( r != ERROR_SUCCESS )
continue;
if (info == MSICOLINFO_NAMES)
MSI_RecordSetStringW( rec, i+1, name );
else
msi_set_record_type_string( rec, i+1, type);
msi_free( name );
}
*prec = rec;
return ERROR_SUCCESS;
}
UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
{
MSIQUERY *query = NULL;
MSIRECORD *rec = NULL;
UINT r;
TRACE("%ld %d %p\n", hView, info, hRec);
if( !hRec )
return ERROR_INVALID_PARAMETER;
if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
return ERROR_INVALID_PARAMETER;
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
if( !query )
return ERROR_INVALID_HANDLE;
r = MSI_ViewGetColumnInfo( query, info, &rec );
if ( r == ERROR_SUCCESS )
{
*hRec = alloc_msihandle( &rec->hdr );
if ( !*hRec )
r = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &rec->hdr );
}
msiobj_release( &query->hdr );
return r;
}
UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
{
MSIVIEW *view = NULL;
if ( !query || !rec )
return ERROR_INVALID_HANDLE;
view = query->view;
if ( !view || !view->ops->modify)
return ERROR_FUNCTION_FAILED;
return view->ops->modify( view, mode, rec );
}
UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
MSIHANDLE hRecord)
{
MSIQUERY *query = NULL;
MSIRECORD *rec = NULL;
UINT r = ERROR_FUNCTION_FAILED;
TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
if( !query )
return ERROR_INVALID_HANDLE;
rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
r = MSI_ViewModify( query, eModifyMode, rec );
msiobj_release( &query->hdr );
if( rec )
msiobj_release( &rec->hdr );
return r;
}
MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
DWORD *pcchBuf )
{
MSIQUERY *query = NULL;
static const WCHAR szError[] = { 0 };
MSIDBERROR r = MSIDBERROR_NOERROR;
int len;
FIXME("%ld %p %p - returns empty error string\n",
handle, szColumnNameBuffer, pcchBuf );
if( !pcchBuf )
return MSIDBERROR_INVALIDARG;
query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
if( !query )
return MSIDBERROR_INVALIDARG;
len = lstrlenW( szError );
if( szColumnNameBuffer )
{
if( *pcchBuf > len )
lstrcpyW( szColumnNameBuffer, szError );
else
r = MSIDBERROR_MOREDATA;
}
*pcchBuf = len;
msiobj_release( &query->hdr );
return r;
}
MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
DWORD *pcchBuf )
{
static const CHAR szError[] = { 0 };
MSIQUERY *query = NULL;
MSIDBERROR r = MSIDBERROR_NOERROR;
int len;
FIXME("%ld %p %p - returns empty error string\n",
handle, szColumnNameBuffer, pcchBuf );
if( !pcchBuf )
return MSIDBERROR_INVALIDARG;
query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
if( !query )
return MSIDBERROR_INVALIDARG;
len = lstrlenA( szError );
if( szColumnNameBuffer )
{
if( *pcchBuf > len )
lstrcpyA( szColumnNameBuffer, szError );
else
r = MSIDBERROR_MOREDATA;
}
*pcchBuf = len;
msiobj_release( &query->hdr );
return r;
}
MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
{
FIXME("\n");
return 0;
}
DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
LPCWSTR szTransformFile, int iErrorCond )
{
HRESULT r;
UINT ret = ERROR_FUNCTION_FAILED;
IStorage *stg = NULL;
STATSTG stat;
TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
r = StgOpenStorage( szTransformFile, NULL,
STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
if ( FAILED(r) )
return ret;
r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
if ( FAILED( r ) )
goto end;
if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
goto end;
if( TRACE_ON( msi ) )
enum_stream_names( stg );
ret = msi_table_apply_transform( db, stg );
end:
IStorage_Release( stg );
return ret;
}
UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
LPCWSTR szTransformFile, int iErrorCond)
{
MSIDATABASE *db;
UINT r;
db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
msiobj_release( &db->hdr );
return r;
}
UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
LPCSTR szTransformFile, int iErrorCond)
{
LPWSTR wstr;
UINT ret;
TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
wstr = strdupAtoW( szTransformFile );
if( szTransformFile && !wstr )
return ERROR_NOT_ENOUGH_MEMORY;
ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
msi_free( wstr );
return ret;
}
UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
LPCSTR szTransformFile, int iReserved1, int iReserved2 )
{
FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
debugstr_a(szTransformFile), iReserved1, iReserved2);
return ERROR_CALL_NOT_IMPLEMENTED;
}
UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
{
FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
debugstr_w(szTransformFile), iReserved1, iReserved2);
return ERROR_CALL_NOT_IMPLEMENTED;
}
UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
{
MSIDATABASE *db;
UINT r;
TRACE("%ld\n", hdb);
db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
/* FIXME: lock the database */
r = MSI_CommitTables( db );
/* FIXME: unlock the database */
msiobj_release( &db->hdr );
if (r == ERROR_SUCCESS)
{
msi_free( db->deletefile );
db->deletefile = NULL;
}
return r;
}
struct msi_primary_key_record_info
{
DWORD n;
MSIRECORD *rec;
};
static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
{
struct msi_primary_key_record_info *info = param;
LPCWSTR name;
DWORD type;
type = MSI_RecordGetInteger( rec, 4 );
if( type & MSITYPE_KEY )
{
info->n++;
if( info->rec )
{
name = MSI_RecordGetString( rec, 3 );
MSI_RecordSetStringW( info->rec, info->n, name );
}
}
return ERROR_SUCCESS;
}
UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
LPCWSTR table, MSIRECORD **prec )
{
static const WCHAR sql[] = {
's','e','l','e','c','t',' ','*',' ',
'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
'w','h','e','r','e',' ',
'`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
struct msi_primary_key_record_info info;
MSIQUERY *query = NULL;
MSIVIEW *view;
UINT r;
r = MSI_OpenQuery( db, &query, sql, table );
if( r != ERROR_SUCCESS )
return r;
view = query->view;
/* count the number of primary key records */
info.n = 0;
info.rec = 0;
r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
if( r == ERROR_SUCCESS )
{
TRACE("Found %d primary keys\n", info.n );
/* allocate a record and fill in the names of the tables */
info.rec = MSI_CreateRecord( info.n );
info.n = 0;
r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
if( r == ERROR_SUCCESS )
*prec = info.rec;
else
msiobj_release( &info.rec->hdr );
}
msiobj_release( &query->hdr );
return r;
}
UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
LPCWSTR table, MSIHANDLE* phRec )
{
MSIRECORD *rec = NULL;
MSIDATABASE *db;
UINT r;
TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
if( r == ERROR_SUCCESS )
{
*phRec = alloc_msihandle( &rec->hdr );
if (! *phRec)
r = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &rec->hdr );
}
msiobj_release( &db->hdr );
return r;
}
UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
LPCSTR table, MSIHANDLE* phRec)
{
LPWSTR szwTable = NULL;
UINT r;
TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
if( table )
{
szwTable = strdupAtoW( table );
if( !szwTable )
return ERROR_OUTOFMEMORY;
}
r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
msi_free( szwTable );
return r;
}
MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
MSIHANDLE hDatabase, LPCSTR szTableName)
{
LPWSTR szwTableName = NULL;
MSICONDITION r;
TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
if( szTableName )
{
szwTableName = strdupAtoW( szTableName );
if( !szwTableName )
return MSICONDITION_ERROR;
}
r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
msi_free( szwTableName );
return r;
}
MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
MSIHANDLE hDatabase, LPCWSTR szTableName)
{
MSIDATABASE *db;
MSICONDITION r;
TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
if( !db )
return MSICONDITION_ERROR;
r = MSI_DatabaseIsTablePersistent( db, szTableName );
msiobj_release( &db->hdr );
return r;
}
--- NEW FILE: msiserver.idl ---
/*
* Copyright (C) 2007 Mike McCormack
* Copyright (C) 2007 Misha Koshelev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "msiserver_dispids.h"
import "unknwn.idl";
import "wtypes.idl";
import "objidl.idl";
import "oaidl.idl";
cpp_quote("#if 0")
typedef unsigned long MSIHANDLE;
typedef int INSTALLMESSAGE;
typedef int MSIRUNMODE;
typedef int INSTALLSTATE;
typedef WORD LANGID;
cpp_quote("#endif")
[
uuid(902B3592-9D08-4dfd-A593-D07C52546421),
oleautomation,
object
]
interface IWineMsiRemotePackage : IUnknown
{
HRESULT SetMsiHandle( [in] MSIHANDLE handle );
HRESULT GetActiveDatabase( [out] MSIHANDLE *handle );
HRESULT GetProperty( [in] BSTR property, [out] BSTR *value, [out] DWORD *size );
HRESULT SetProperty( [in] BSTR property, [in] BSTR value );
HRESULT ProcessMessage( [in] INSTALLMESSAGE message, [in] MSIHANDLE record );
HRESULT DoAction( [in] BSTR action );
HRESULT Sequence( [in] BSTR table, [in] int sequence );
HRESULT GetTargetPath( [in] BSTR folder, [out] BSTR *value, [out] DWORD *size );
HRESULT SetTargetPath( [in] BSTR folder, [in] BSTR value );
HRESULT GetSourcePath( [in] BSTR folder, [out] BSTR *value, [out] DWORD *size );
HRESULT GetMode( [in] MSIRUNMODE mode, [out] BOOL *ret );
HRESULT GetFeatureState( [in] BSTR feature, [out] INSTALLSTATE *installed, [out] INSTALLSTATE *action );
HRESULT SetFeatureState( [in] BSTR feature, [in] INSTALLSTATE state );
HRESULT GetComponentState( [in] BSTR component, [out] INSTALLSTATE *installed, [out] INSTALLSTATE *action );
HRESULT SetComponentState( [in] BSTR component, [in] INSTALLSTATE state );
HRESULT GetLanguage( [out] LANGID *language );
HRESULT SetInstallLevel( [in] int level );
HRESULT FormatRecord( [in] MSIHANDLE record, [out] BSTR value, [out] DWORD *size );
HRESULT EvaluateCondition( [in] BSTR condition );
}
[
uuid(56D58B64-8780-4c22-A8BC-8B0B29E4A9F8),
oleautomation,
object
]
interface IWineMsiRemoteCustomAction : IUnknown
{
HRESULT GetActionInfo( [in] LPCGUID guid, [out] INT *type, [out] MSIHANDLE *handle, [out] BSTR *dllname,
[out] BSTR *function, [out] IWineMsiRemotePackage **package );
}
[ uuid(000C1092-0000-0000-C000-000000000046), version(1.0) ]
library WindowsInstaller
{
dispinterface Installer;
dispinterface Record;
dispinterface Session;
dispinterface Database;
dispinterface SummaryInfo;
dispinterface View;
dispinterface UIPreview;
dispinterface FeatureInfo;
dispinterface RecordList;
dispinterface StringList;
dispinterface Product;
dispinterface Patch;
typedef enum {
msiInstallStateNotUsed = -7,
msiInstallStateBadConfig = -6,
msiInstallStateIncomplete = -5,
msiInstallStateSourceAbsent = -4,
msiInstallStateInvalidArg = -2,
msiInstallStateUnknown = -1,
msiInstallStateBroken = 0,
msiInstallStateAdvertised = 1,
msiInstallStateRemoved = 1,
msiInstallStateAbsent = 2,
msiInstallStateLocal = 3,
msiInstallStateSource = 4,
msiInstallStateDefault = 5
} MsiInstallState;
typedef enum {
msiOpenDatabaseModeReadOnly = 0,
msiOpenDatabaseModeTransact = 1,
msiOpenDatabaseModeDirect = 2,
msiOpenDatabaseModeCreate = 3,
msiOpenDatabaseModeCreateDirect = 4,
msiOpenDatabaseModePatchFile = 32
} MsiOpenDatabaseMode;
[ uuid(000C1090-0000-0000-C000-000000000046) ]
dispinterface Installer
{
properties:
methods:
[id(DISPID_INSTALLER_CREATERECORD)]
Record *CreateRecord([in] long Count);
[id(DISPID_INSTALLER_OPENPACKAGE)]
Session* OpenPackage(
[in] VARIANT PackagePath,
[in, optional, defaultvalue(0)] long Options);
[id(DISPID_INSTALLER_OPENDATABASE)]
Database *OpenDatabase(
[in] BSTR DatabasePath,
[in] VARIANT OpenMode);
[id(DISPID_INSTALLER_INSTALLPRODUCT)]
void InstallProduct(
[in] BSTR PackagePath,
[in, optional, defaultvalue("0")] BSTR PropertyValues);
[id(DISPID_INSTALLER_REGISTRYVALUE), propget]
BSTR RegistryValue(
[in] VARIANT Root,
[in] BSTR Key,
[in, optional] VARIANT Value);
[id(DISPID_INSTALLER_PRODUCTSTATE), propget]
MsiInstallState ProductState(
[in] BSTR Product);
[id(DISPID_INSTALLER_PRODUCTINFO), propget]
BSTR ProductInfo(
[in] BSTR Product,
[in] BSTR Attribute);
[id(DISPID_INSTALLER_PRODUCTS), propget]
StringList *Products();
[id(DISPID_INSTALLER_RELATEDPRODUCTS), propget]
StringList *RelatedProducts(
[in] BSTR UpgradeCode);
}
[ uuid(000C1093-0000-0000-C000-000000000046) ]
dispinterface Record
{
properties:
methods:
[id(DISPID_RECORD_STRINGDATA), propget]
BSTR StringData([in] long Field);
[id(DISPID_RECORD_STRINGDATA), propput]
void StringData(
[in] long Field,
[in] BSTR rhs);
[id(DISPID_RECORD_INTEGERDATA), propget]
long IntegerData([in] long Field);
[id(DISPID_RECORD_INTEGERDATA), propput]
void IntegerData(
[in] long Field,
[in] long rhs);
[id(DISPID_RECORD_FIELDCOUNT), propget]
long FieldCount();
}
[ uuid(000C1095-0000-0000-C000-000000000046) ]
dispinterface StringList
{
properties:
methods:
[id(DISPID_LIST__NEWENUM)]
IUnknown _NewEnum();
[id(DISPID_LIST_ITEM), propget]
BSTR Item(long Index);
[id(DISPID_LIST_COUNT), propget]
long Count();
}
[ uuid(000C1096-0000-0000-C000-000000000046) ]
dispinterface RecordList
{
properties:
methods:
}
[ uuid(000C109A-0000-0000-C000-000000000046) ]
dispinterface UIPreview
{
properties:
methods:
}
[ uuid(000C109B-0000-0000-C000-000000000046) ]
dispinterface SummaryInfo
{
properties:
methods:
[id(DISPID_SUMMARYINFO_PROPERTY), propget]
VARIANT Property([in] long Pid);
[id(DISPID_SUMMARYINFO_PROPERTY), propput]
void Property(
[in] long Pid,
[in] VARIANT rhs);
[id(DISPID_SUMMARYINFO_PROPERTYCOUNT), propget]
long PropertyCount();
}
typedef enum {
msiViewModifySeek = -1,
msiViewModifyRefresh = 0,
msiViewModifyInsert = 1,
msiViewModifyUpdate = 2,
msiViewModifyAssign = 3,
msiViewModifyReplace = 4,
msiViewModifyMerge = 5,
msiViewModifyDelete = 6,
msiViewModifyInsertTemporary = 7,
msiViewModifyValidate = 8,
msiViewModifyValidateNew = 9,
msiViewModifyValidateField = 10,
msiViewModifyValidateDelete = 11,
} _MsiViewModify; /* Added underscore to avoid conflict with function name */
[ uuid(000C109C-0000-0000-C000-000000000046) ]
dispinterface View
{
properties:
methods:
[id(DISPID_VIEW_EXECUTE)]
void Execute([in, optional, defaultvalue(0)] Record *Params);
[id(DISPID_VIEW_FETCH)]
Record* Fetch();
[id(DISPID_VIEW_MODIFY)]
void Modify(
[in] _MsiViewModify Mode,
Record *Record);
[id(DISPID_VIEW_CLOSE)]
void Close();
}
[ uuid(000C109D-0000-0000-C000-000000000046) ]
dispinterface Database
{
properties:
methods:
[id(DISPID_DATABASE_OPENVIEW)]
View* OpenView([in] BSTR Sql);
[id(DISPID_DATABASE_SUMMARYINFORMATION), propget]
SummaryInfo *SummaryInformation([in, optional, defaultvalue(0)] long UpdateCount);
}
typedef enum {
msiDoActionStatusNoAction = 0,
msiDoActionStatusSuccess = 1,
msiDoActionStatusUserExit = 2,
msiDoActionStatusFailure = 3,
msiDoActionStatusSuspend = 4,
msiDoActionStatusFinished = 5,
msiDoActionStatusWrongState = 6,
msiDoActionStatusBadActionData = 7
} MsiDoActionStatus;
typedef enum {
msiRunModeAdmin = 0,
msiRunModeAdvertise = 1,
msiRunModeMaintenance = 2,
msiRunModeRollbackEnabled = 3,
msiRunModeLogEnabled = 4,
msiRunModeOperations = 5,
msiRunModeRebootAtEnd = 6,
msiRunModeRebootNow = 7,
msiRunModeCabinet = 8,
msiRunModeSourceShortNames = 9,
msiRunModeTargetShortNames = 10,
msiRunModeWindows9x = 12,
msiRunModeZawEnabled = 13,
msiRunModeScheduled = 16,
msiRunModeRollback = 17,
msiRunModeCommit = 18
} MsiRunMode;
typedef enum {
msiEvaluateConditionFalse = 0,
msiEvaluateConditionTrue = 1,
msiEvaluateConditionNone = 2,
msiEvaluateConditionError = 3
} _MsiEvaluateCondition; /* Added underscore to avoid conflict with function name */
[ uuid(000C109E-0000-0000-C000-000000000046) ]
dispinterface Session
{
properties:
methods:
[id(DISPID_SESSION_INSTALLER), propget]
Installer *Installer();
[id(DISPID_SESSION_PROPERTY), propget]
BSTR Property([in] BSTR Name);
[id(DISPID_SESSION_PROPERTY), propput]
void Property(
[in] BSTR Name,
[in] BSTR rhs);
[id(DISPID_SESSION_LANGUAGE), propget]
long Language();
[id(DISPID_SESSION_MODE), propget]
VARIANT_BOOL Mode([in] MsiRunMode Flag);
[id(DISPID_SESSION_MODE), propput]
void Mode(
[in] MsiRunMode Flag,
[in] VARIANT_BOOL rhs);
[id(DISPID_SESSION_DATABASE), propget]
Database* Database();
[id(DISPID_SESSION_DOACTION)]
MsiDoActionStatus DoAction([in] BSTR Action);
[id(DISPID_SESSION_EVALUATECONDITION)]
_MsiEvaluateCondition EvaluateCondition([in] BSTR Expression);
[id(DISPID_SESSION_FEATURECURRENTSTATE), propget]
MsiInstallState FeatureCurrentState([in] BSTR Feature);
[id(DISPID_SESSION_FEATUREREQUESTSTATE), propget]
MsiInstallState FeatureRequestState([in] BSTR Feature);
[id(DISPID_SESSION_FEATUREREQUESTSTATE), propput]
void FeatureRequestState(
[in] BSTR Feature,
[in] MsiInstallState rhs);
[id(DISPID_SESSION_SETINSTALLLEVEL)]
void SetInstallLevel([in] long Level);
}
[ uuid(000C109F-0000-0000-C000-000000000046) ]
dispinterface FeatureInfo
{
properties:
methods:
}
[ uuid(000C10A0-0000-0000-C000-000000000046) ]
dispinterface Product
{
properties:
methods:
}
[ uuid(000C10A1-0000-0000-C000-000000000046) ]
dispinterface Patch
{
properties:
methods:
}
}
--- NEW FILE: msiserver_dispids.h ---
/*
* Copyright (C) 2007 Misha Koshelev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define DISPID_INSTALLER_CREATERECORD 1
#define DISPID_INSTALLER_OPENPACKAGE 2
#define DISPID_INSTALLER_OPENDATABASE 4
#define DISPID_INSTALLER_INSTALLPRODUCT 8
#define DISPID_INSTALLER_REGISTRYVALUE 11
#define DISPID_INSTALLER_PRODUCTSTATE 17
#define DISPID_INSTALLER_PRODUCTINFO 18
#define DISPID_INSTALLER_PRODUCTS 35
#define DISPID_INSTALLER_RELATEDPRODUCTS 40
#define DISPID_RECORD_FIELDCOUNT 0
#define DISPID_RECORD_STRINGDATA 1
#define DISPID_RECORD_INTEGERDATA 2
#define DISPID_LIST__NEWENUM -4
#define DISPID_LIST_ITEM 0
#define DISPID_LIST_COUNT 1
#define DISPID_VIEW_EXECUTE 1
#define DISPID_VIEW_FETCH 2
#define DISPID_VIEW_MODIFY 3
#define DISPID_VIEW_CLOSE 4
#define DISPID_DATABASE_SUMMARYINFORMATION 2
#define DISPID_DATABASE_OPENVIEW 3
#define DISPID_SESSION_INSTALLER 1
#define DISPID_SESSION_PROPERTY 2
#define DISPID_SESSION_LANGUAGE 3
#define DISPID_SESSION_MODE 4
#define DISPID_SESSION_DATABASE 5
#define DISPID_SESSION_DOACTION 8
#define DISPID_SESSION_EVALUATECONDITION 10
#define DISPID_SESSION_FEATURECURRENTSTATE 13
#define DISPID_SESSION_FEATUREREQUESTSTATE 14
#define DISPID_SESSION_SETINSTALLLEVEL 19
#define DISPID_SUMMARYINFO_PROPERTY 1
#define DISPID_SUMMARYINFO_PROPERTYCOUNT 2
--- NEW FILE: msi_Bg.rc ---
/*
* Bulgarian resources for MSI
*
* Copyright 2005 Milko Krachounov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "ïúòÿò %s íå å íàìåðåí"
9 "ïîñòàâåòå äèñê %s"
10 "íåêîðåêòíè ïàðàìåòðè"
11 "âúâåäåòå ïàïêàòà, êîÿòî ñúäúðæà %s"
12 "èçòî÷íèêà çà èíñòàëàöèÿ íà ôóíêöèîíàëíîñòòà ëèïñâà"
13 "ìðåæîâîòî óñòðîéñòâà íóæíî çà ôóíêöèîíàëíîñòòà ëèïñâà "
14 "ôóíêöèîíàëíîñò îò:"
15 "èçáåðåòå ïàïêàòà, êîÿòî ñúäúðæà %s"
}
--- NEW FILE: msi_De.rc ---
/*
* German resources for MSI
*
* Copyright 2005 Henning Gerhardt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "Der Pfad %s wurde nicht gefunden."
9 "Bitte Disk %s einlegen."
10 "schlechte Parameter"
11 "Geben Sie das Verzeichnis ein, dass %s enthält."
12 "Die Installationsquelle für das Feature fehlt."
13 "Das Netzwerklaufwerk für das Feature fehlt."
14 "Feature von:"
15 "Wählen Sie das Verzeichnis aus, dass %s enthält."
}
--- NEW FILE: msi_En.rc ---
/*
* English resources for MSI
*
* Copyright 2005 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "path %s not found"
9 "insert disk %s"
10 "bad parameters"
11 "enter which folder contains %s"
12 "install source for feature missing"
13 "network drive for feature missing"
14 "feature from:"
15 "choose which folder contains %s"
}
--- NEW FILE: msi_Eo.rc ---
/*
* Esperanto resources for MSI
*
* Copyright 2006 Antonio Codazzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_ESPERANTO, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "Mi ne trovis la vojon %s"
9 "enþovu la diskon %s"
10 "nekorektaj parametroj"
11 "enigu la nomon de dosierujo kiu enhavas %s"
12 "instalad-fonto por mankanta taýgeco"
13 "retdrajvo por mankanta taýgeco"
14 "taýgeco el:"
15 "elektu la dosierujo kiu enhavas %s"
}
--- NEW FILE: msi_Es.rc ---
/*
* Spanish resources for MSI
*
* Copyright 2005, 2006 José Manuel Ferrer Ortiz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 "No se ha podido abrir el paquete de instalación especificado. Por favor, compruebe la ruta del archivo y vuelva a intentarlo."
5 "ruta %s no encontrada"
9 "inserte el disco %s"
10 "parámetros incorrectos"
11 "introduzca qué carpeta contiene %s"
12 "instalar fuente para característica ausente"
13 "unidad de red para característica ausente"
14 "característica de:"
15 "elija qué carpeta contiene %s"
}
--- NEW FILE: msi_Fi.rc ---
/*
* Finnish resources for MSI
*
* Copyright 2005 Kimmo Myllyvirta
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "Polkua %s ei löydy."
9 "Anna levy %s"
10 "Virheelliset parametrit."
11 "Anna kansio, joka sisältää %s"
12 "Ominaisuuden asennuslähde puuttuu."
13 "Ominaisuuden verkkolevy puuttuu."
14 "Ominaisuus:"
15 "Valitse kansio, joka sisältää %s"
}
--- NEW FILE: msi_Fr.rc ---
/*
* French resources for MSI
*
* Copyright 2005 Jonathan Ernst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "Le chemin %s est introuvable"
9 "insérez le disque %s"
10 "mauvais paramètres"
11 "saisissez le nom du dossier contenant %s"
12 "source d'installation pour la fonctionnalité manquante"
13 "lecteur réseau pour la fonctionnalité manquant"
14 "fonctionnalité depuis:"
15 "sélectionnez le dossier contenant %s"
}
--- NEW FILE: msi_Hu.rc ---
/*
* Hungarian resources for MSI
*
* Copyright 2006 Andras Kovacs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "%s útvonal nem található"
9 "helyezze be a lemezt: %s"
10 "rossz paraméterek"
11 "adja meg melyik mappa tartalmazza ezt: %s"
12 "hiányzó tulajdonság a telepítési forráshoz"
13 "hiányzó tulajdonság a hálózati meghajtóhoz"
14 "tulajdonság innen:"
15 "válassza ki, melyik mappa tartalmazza ezt: %s"
}
--- NEW FILE: msi_It.rc ---
/*
* Italian resources for MSI
*
* Copyright 2006 Antonio Codazzi
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "percorso %s non trovato"
9 "inserire disco %s"
10 "parametri incorretti"
11 "immettere il nome della cartella che contiene %s"
12 "sorgente di installazione per la funzionalità mancante"
13 "periferica di rete per la funzionalità mancante"
14 "funzionalità da:"
15 "selezionare la cartella che contiene %s"
}
--- NEW FILE: msi_Ko.rc ---
/*
* Korean resources for MSI
*
* Copyright 2005 YunSong Hwang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_KOREAN, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 " ÁöÁ¤ÇÑ ¼³Ä¡ ÆÐŰÁö¸¦ ¿ ¼ö ¾ø½À´Ï´Ù. ÆÄÀÏ °æ·Î¸¦ È®ÀÎÇÏ°í ´Ù½Ã ½ÃµµÇϽʽÿÀ."
5 "%s °æ·Î¸¦ ãÀ»¼ö ¾ø½À´Ï´Ù"
9 "µð½ºÅ© %s »ðÀÔ"
10 "Àý¸øµÈ ¸Å°³º¯¼ö"
11 "%s¸¦ Æ÷ÇÔÇÏ´Â Æú´õ¸¦ ÀÔ·ÂÇϼ¼¿©"
12 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ¼³Ä¡ ¿øº»"
13 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ³×Æ®¿öÅ© µå¶óÀ̺ê"
14 "ºÎºÐ(feature)¿¡¼:"
15 " %s¸¦ Æ÷ÇÔÇÏ´Â Æú´õ ¼±ÅÃ"
}
--- NEW FILE: msi_main.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2006 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "objbase.h"
#include "shlwapi.h"
#include "oleauto.h"
#include "msipriv.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static LONG dll_count;
/* the UI level */
INSTALLUILEVEL gUILevel = INSTALLUILEVEL_BASIC;
HWND gUIhwnd = 0;
INSTALLUI_HANDLERA gUIHandlerA = NULL;
INSTALLUI_HANDLERW gUIHandlerW = NULL;
DWORD gUIFilter = 0;
LPVOID gUIContext = NULL;
WCHAR gszLogFile[MAX_PATH];
WCHAR msi_path[MAX_PATH];
ITypeLib *msi_typelib = NULL;
HINSTANCE msi_hInstance;
/*
* Dll lifetime tracking declaration
*/
static void LockModule(void)
{
InterlockedIncrement(&dll_count);
}
static void UnlockModule(void)
{
InterlockedDecrement(&dll_count);
}
/******************************************************************
* DllMain
*/
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
msi_hInstance = hinstDLL;
DisableThreadLibraryCalls(hinstDLL);
msi_dialog_register_class();
break;
case DLL_PROCESS_DETACH:
if (msi_typelib) ITypeLib_Release( msi_typelib );
msi_dialog_unregister_class();
msi_free_handle_table();
break;
}
return TRUE;
}
static CRITICAL_SECTION MSI_typelib_cs;
static CRITICAL_SECTION_DEBUG MSI_typelib_cs_debug =
{
0, 0, &MSI_typelib_cs,
{ &MSI_typelib_cs_debug.ProcessLocksList,
&MSI_typelib_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": MSI_typelib_cs") }
};
static CRITICAL_SECTION MSI_typelib_cs = { &MSI_typelib_cs_debug, -1, 0, 0, 0, 0 };
ITypeLib *get_msi_typelib( LPWSTR *path )
{
EnterCriticalSection( &MSI_typelib_cs );
if (!msi_typelib)
{
TRACE("loading typelib\n");
if (GetModuleFileNameW( msi_hInstance, msi_path, MAX_PATH ))
LoadTypeLib( msi_path, &msi_typelib );
}
LeaveCriticalSection( &MSI_typelib_cs );
if (path)
*path = msi_path;
if (msi_typelib)
ITypeLib_AddRef( msi_typelib );
return msi_typelib;
}
typedef struct tagIClassFactoryImpl {
const IClassFactoryVtbl *lpVtbl;
HRESULT (*create_object)( IUnknown*, LPVOID* );
} IClassFactoryImpl;
static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface,
REFIID riid,LPVOID *ppobj)
{
IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
TRACE("%p %s %p\n",This,debugstr_guid(riid),ppobj);
if( IsEqualCLSID( riid, &IID_IUnknown ) ||
IsEqualCLSID( riid, &IID_IClassFactory ) )
{
IClassFactory_AddRef( iface );
*ppobj = iface;
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface)
{
LockModule();
return 2;
}
static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface)
{
UnlockModule();
return 1;
}
static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface,
LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj)
{
IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
IUnknown *unk = NULL;
HRESULT r;
TRACE("%p %p %s %p\n", This, pOuter, debugstr_guid(riid), ppobj);
r = This->create_object( pOuter, (LPVOID*) &unk );
if (SUCCEEDED(r))
{
r = IUnknown_QueryInterface( unk, riid, ppobj );
IUnknown_Release( unk );
}
return r;
}
static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
{
TRACE("%p %d\n", iface, dolock);
if (dolock)
LockModule();
else
UnlockModule();
return S_OK;
}
static const IClassFactoryVtbl MsiCF_Vtbl =
{
MsiCF_QueryInterface,
MsiCF_AddRef,
MsiCF_Release,
MsiCF_CreateInstance,
MsiCF_LockServer
};
static IClassFactoryImpl MsiServer_CF = { &MsiCF_Vtbl, create_msiserver };
static IClassFactoryImpl WineMsiCustomRemote_CF = { &MsiCF_Vtbl, create_msi_custom_remote };
static IClassFactoryImpl WineMsiRemotePackage_CF = { &MsiCF_Vtbl, create_msi_remote_package };
/******************************************************************
* DllGetClassObject [MSI.@]
*/
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
TRACE("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
if ( IsEqualCLSID (rclsid, &CLSID_IMsiServerX2) )
{
*ppv = (LPVOID) &MsiServer_CF;
return S_OK;
}
if ( IsEqualCLSID (rclsid, &CLSID_IWineMsiRemoteCustomAction) )
{
*ppv = (LPVOID) &WineMsiCustomRemote_CF;
return S_OK;
}
if ( IsEqualCLSID (rclsid, &CLSID_IWineMsiRemotePackage) )
{
*ppv = (LPVOID) &WineMsiRemotePackage_CF;
return S_OK;
}
if( IsEqualCLSID (rclsid, &CLSID_IMsiServerMessage) ||
IsEqualCLSID (rclsid, &CLSID_IMsiServer) ||
IsEqualCLSID (rclsid, &CLSID_IMsiServerX1) ||
IsEqualCLSID (rclsid, &CLSID_IMsiServerX3) )
{
FIXME("create %s object\n", debugstr_guid( rclsid ));
}
return CLASS_E_CLASSNOTAVAILABLE;
}
/******************************************************************
* DllGetVersion [MSI.@]
*/
HRESULT WINAPI DllGetVersion(DLLVERSIONINFO *pdvi)
{
TRACE("%p\n",pdvi);
if (pdvi->cbSize < sizeof(DLLVERSIONINFO))
return E_INVALIDARG;
pdvi->dwMajorVersion = MSI_MAJORVERSION;
pdvi->dwMinorVersion = MSI_MINORVERSION;
pdvi->dwBuildNumber = MSI_BUILDNUMBER;
pdvi->dwPlatformID = DLLVER_PLATFORM_WINDOWS;
return S_OK;
}
/******************************************************************
* DllCanUnloadNow [MSI.@]
*/
HRESULT WINAPI DllCanUnloadNow(void)
{
return dll_count == 0 ? S_OK : S_FALSE;
}
--- NEW FILE: msi_Nl.rc ---
/*
* Durch resources for MSI
*
* Copyright 2005 Hans Leidekker
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "Pad %s niet gevonden"
9 "Plaats disk %s"
10 "Ongeldige parameters"
11 "Voer de map in die %s bevat"
12 "De installatiebron van het feature ontbreekt"
13 "De netwerkschijf met het feature ontbreekt"
14 "Feature van:"
15 "Kies de map die %s bevat"
}
--- NEW FILE: msi_No.rc ---
/*
* Norwegian Bokmål resources for MSI
*
* Copyright 2005-2007 Alexander N. Sørnes <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
STRINGTABLE DISCARDABLE
{
4 "Klarte ikke åpne den oppgitte installasjonspakken. Kontroller filbanen og prøv igjen."
5 "Fant ikke stien '%s'."
9 "Sett i disk '%s'"
10 "Gale parametere."
11 "Oppgi katalogen som inneholder '%s'."
12 "Egenskapens installasjonskilde mangler."
13 "Egenskapens nettverksstasjon mangler."
14 "Egenskap fra::"
15 "Velg katalogen som inneholder '%s'."
}
--- NEW FILE: msi_Pl.rc ---
/*
* Polish resources for MSI
*
* Copyright 2005 Mike McCormack
* Copyright 2007 Mikolaj Zalewski
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "Nie uda³o siê otworzyæ wybranego pakietu instalacyjnego. Sprawd czy cie¿ka jest poprawna i spróbuj ponownie."
5 "cie¿ka '%s' nie zosta³a odnaleziona"
9 "w³ó¿ dysk '%s'"
10 "z³e parametry"
11 "wprowad sice¿kê do folderu zawieraj¹cego '%s'"
12 "ród³o danych zawieraj¹ce ¿¹danê funkcjê jest niedostêpne"
13 "dysk siecowy zawieraj¹cy ¿¹dan¹ funckje jest niedostêpny"
14 "funkcja z:"
15 "wybierz folder zawieraj¹cy '%s'"
}
--- NEW FILE: msi_Pt.rc ---
/*
* Portuguese resources for MSI
*
* Copyright 2005 Marcelo Duarte
* Copyright 2006 Américo José Melo
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "caminho %s não encontrado"
9 "insira disco %s"
10 "parâmetros inválidos"
11 "entre qual pasta contém %s"
12 "instalar fonte para característica faltando"
13 "drive de rede para característica faltando"
14 "característica de:"
15 "escolha qual pasta contém %s"
}
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "localização %s não encontrada"
9 "insira o disco %s"
10 "parâmetros inválidos"
11 "indique que pasta contém %s"
12 "instalar fonte para a opção em falta"
13 "controlador de rede para a opção em falta"
14 "opção de:"
15 "indique que pasta contém %s"
}
--- NEW FILE: msi_Ru.rc ---
/*
* Russian resources for MSI
*
* Copyright 2006 Vitaly Lipatov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "Óêàçàííûé ïàêåò íå ìîæåò áûòü îòêðûò. Ïðîâåðüòå ôàéë è ïîâòîðèòå ïîïûòêó."
5 "ïóòü %s íå íàéäåí"
9 "âñòàâüòå äèñê %s"
10 "íåâåðíûå ïàðàìåòðû"
11 "óêàæèòå êàòàëîã, ñîäåðæàùèé %s"
12 "èñòî÷íèê óñòàíîâêè äàííîé âîçìîæíîñòè íå óêàçàí"
13 "ñåòåâîé äèñê äëÿ äàííîé âîçìîæíîñòè íå óêàçàí"
14 "âîçìîæíîñòü èç:"
15 "âûáåðèòå êàòàëîã, ñîäåðæàùèé %s"
}
--- NEW FILE: msi_Tr.rc ---
/*
* Turkish resources for MSI
*
* Copyright 2006 Fatih Aþýcý
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT
STRINGTABLE DISCARDABLE
{
4 "The specified installation package could not be opened. Please check the file path and try again."
5 "%s yolu bulunamadý"
9 "%s nolu diski yerleþtirin"
10 "bozuk parametreler"
11 "%s öðesini içeren dizini girin"
12 "eksik özellik için kurulum kaynaðý"
13 "eksik özellik için að sürücüsü"
14 "özellik buradan:"
15 "%s öðesini içeren dizini seçin"
}
--- NEW FILE: order.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSIORDERVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
UINT *reorder;
UINT num_cols;
UINT cols[1];
} MSIORDERVIEW;
static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
{
UINT r, i, a_val = 0, b_val = 0;
*swap = 0;
for( i=0; i<ov->num_cols; i++ )
{
r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
if( r != ERROR_SUCCESS )
return r;
r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
if( r != ERROR_SUCCESS )
return r;
if( a_val != b_val )
{
if( a_val > b_val )
*swap = 1;
break;
}
}
return ERROR_SUCCESS;
}
static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
{
UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
UINT *array = ov->reorder;
if( left == right )
return ERROR_SUCCESS;
/* sort the left half */
r = ORDER_mergesort( ov, left, centre );
if( r != ERROR_SUCCESS )
return r;
/* sort the right half */
r = ORDER_mergesort( ov, centre+1, right );
if( r != ERROR_SUCCESS )
return r;
for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
{
r = ORDER_compare( ov, array[i], array[j], &swap );
if( r != ERROR_SUCCESS )
return r;
if( swap )
{
temp = array[j];
memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
array[i] = temp;
j++;
centre++;
}
}
return ERROR_SUCCESS;
}
static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
{
UINT i, swap, r;
for( i=1; i<num_rows; i++ )
{
r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
if( r != ERROR_SUCCESS )
return r;
if( !swap )
continue;
ERR("Bad order! %d\n", i);
return ERROR_FUNCTION_FAILED;
}
return ERROR_SUCCESS;
}
static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p %d %d %p\n", ov, row, col, val );
if( !ov->table )
return ERROR_FUNCTION_FAILED;
row = ov->reorder[ row ];
return ov->table->ops->fetch_int( ov->table, row, col, val );
}
static UINT ORDER_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
UINT r, num_rows = 0, i;
TRACE("%p %p\n", ov, record);
if( !ov->table )
return ERROR_FUNCTION_FAILED;
r = ov->table->ops->execute( ov->table, record );
if( r != ERROR_SUCCESS )
return r;
r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
if( r != ERROR_SUCCESS )
return r;
ov->reorder = msi_alloc( num_rows*sizeof(UINT) );
if( !ov->reorder )
return ERROR_FUNCTION_FAILED;
for( i=0; i<num_rows; i++ )
ov->reorder[i] = i;
r = ORDER_mergesort( ov, 0, num_rows - 1 );
if( r != ERROR_SUCCESS )
return r;
r = ORDER_verify( ov, num_rows );
if( r != ERROR_SUCCESS )
return r;
return ERROR_SUCCESS;
}
static UINT ORDER_close( struct tagMSIVIEW *view )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p\n", ov );
if( !ov->table )
return ERROR_FUNCTION_FAILED;
msi_free( ov->reorder );
ov->reorder = NULL;
return ov->table->ops->close( ov->table );
}
static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p %p %p\n", ov, rows, cols );
if( !ov->table )
return ERROR_FUNCTION_FAILED;
return ov->table->ops->get_dimensions( ov->table, rows, cols );
}
static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p %d %p %p\n", ov, n, name, type );
if( !ov->table )
return ERROR_FUNCTION_FAILED;
return ov->table->ops->get_column_info( ov->table, n, name, type );
}
static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p %d %p\n", ov, eModifyMode, rec );
if( !ov->table )
return ERROR_FUNCTION_FAILED;
return ov->table->ops->modify( ov->table, eModifyMode, rec );
}
static UINT ORDER_delete( struct tagMSIVIEW *view )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
TRACE("%p\n", ov );
if( ov->table )
ov->table->ops->delete( ov->table );
msi_free( ov->reorder );
ov->reorder = NULL;
msiobj_release( &ov->db->hdr );
msi_free( ov );
return ERROR_SUCCESS;
}
static UINT ORDER_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
UINT r;
TRACE("%p, %d, %u, %p\n", ov, col, val, *handle);
if( !ov->table )
return ERROR_FUNCTION_FAILED;
r = ov->table->ops->find_matching_rows( ov->table, col, val, row, handle );
*row = ov->reorder[ *row ];
return r;
}
static const MSIVIEWOPS order_ops =
{
ORDER_fetch_int,
NULL,
NULL,
NULL,
NULL,
ORDER_execute,
ORDER_close,
ORDER_get_dimensions,
ORDER_get_column_info,
ORDER_modify,
ORDER_delete,
ORDER_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
static UINT ORDER_AddColumn( MSIORDERVIEW *ov, LPCWSTR name )
{
UINT n, count, r;
MSIVIEW *table;
TRACE("%p adding %s\n", ov, debugstr_w( name ) );
if( ov->view.ops != &order_ops )
return ERROR_FUNCTION_FAILED;
table = ov->table;
if( !table )
return ERROR_FUNCTION_FAILED;
if( !table->ops->get_dimensions )
return ERROR_FUNCTION_FAILED;
if( !table->ops->get_column_info )
return ERROR_FUNCTION_FAILED;
r = table->ops->get_dimensions( table, NULL, &count );
if( r != ERROR_SUCCESS )
return r;
if( ov->num_cols >= count )
return ERROR_FUNCTION_FAILED;
r = VIEW_find_column( table, name, &n );
if( r != ERROR_SUCCESS )
return r;
ov->cols[ov->num_cols] = n;
TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
ov->num_cols++;
return ERROR_SUCCESS;
}
UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
column_info *columns )
{
MSIORDERVIEW *ov = NULL;
UINT count = 0, r;
column_info *x;
TRACE("%p\n", ov );
r = table->ops->get_dimensions( table, NULL, &count );
if( r != ERROR_SUCCESS )
{
ERR("can't get table dimensions\n");
return r;
}
ov = msi_alloc_zero( sizeof *ov + sizeof (UINT) * count );
if( !ov )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
ov->view.ops = &order_ops;
msiobj_addref( &db->hdr );
ov->db = db;
ov->table = table;
ov->reorder = NULL;
ov->num_cols = 0;
*view = (MSIVIEW*) ov;
for( x = columns; x ; x = x->next )
ORDER_AddColumn( ov, x->column );
return ERROR_SUCCESS;
}
--- NEW FILE: package.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2004 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...1772 lines suppressed...]
}
UINT msi_package_add_media_disk(MSIPACKAGE *package, DWORD context, DWORD options,
DWORD disk_id, LPWSTR volume_label, LPWSTR disk_prompt)
{
MSIMEDIADISK *disk;
disk = msi_alloc(sizeof(MSIMEDIADISK));
if (!disk)
return ERROR_OUTOFMEMORY;
disk->context = context;
disk->options = options;
disk->disk_id = disk_id;
disk->volume_label = strdupW(volume_label);
disk->disk_prompt = strdupW(disk_prompt);
list_add_head(&package->sourcelist_media, &disk->entry);
return ERROR_SUCCESS;
}
--- NEW FILE: preview.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine-lgpl/msi.h"
#include "msipriv.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static void MSI_ClosePreview( MSIOBJECTHDR *arg )
{
MSIPREVIEW *preview = (MSIPREVIEW *) arg;
msiobj_release( &preview->package->hdr );
}
MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE *db )
{
MSIPREVIEW *preview = NULL;
MSIPACKAGE *package;
package = MSI_CreatePackage( db, NULL );
if( package )
{
preview = alloc_msiobject( MSIHANDLETYPE_PREVIEW, sizeof (MSIPREVIEW),
MSI_ClosePreview );
if( preview )
{
preview->package = package;
preview->dialog = 0;
msiobj_addref( &package->hdr );
}
msiobj_release( &package->hdr );
}
return preview;
}
UINT WINAPI MsiEnableUIPreview( MSIHANDLE hdb, MSIHANDLE* phPreview )
{
MSIDATABASE *db;
MSIPREVIEW *preview;
UINT r = ERROR_FUNCTION_FAILED;
TRACE("%ld %p\n", hdb, phPreview);
db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_HANDLE;
preview = MSI_EnableUIPreview( db );
if( preview )
{
*phPreview = alloc_msihandle( &preview->hdr );
msiobj_release( &preview->hdr );
r = ERROR_SUCCESS;
if (! *phPreview)
r = ERROR_NOT_ENOUGH_MEMORY;
}
msiobj_release( &db->hdr );
return r;
}
static UINT preview_event_handler( MSIPACKAGE *package, LPCWSTR event,
LPCWSTR argument, msi_dialog *dialog )
{
MESSAGE("Preview dialog event '%s' (arg='%s')\n",
debugstr_w( event ), debugstr_w( argument ));
return ERROR_SUCCESS;
}
UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName )
{
msi_dialog *dialog = NULL;
UINT r = ERROR_SUCCESS;
if( preview->dialog )
msi_dialog_destroy( preview->dialog );
/* an empty name means we should just destroy the current preview dialog */
if( szDialogName )
{
dialog = msi_dialog_create( preview->package, szDialogName, NULL,
preview_event_handler );
if( dialog )
msi_dialog_do_preview( dialog );
else
r = ERROR_FUNCTION_FAILED;
}
preview->dialog = dialog;
return r;
}
UINT WINAPI MsiPreviewDialogW( MSIHANDLE hPreview, LPCWSTR szDialogName )
{
MSIPREVIEW *preview;
UINT r;
TRACE("%ld %s\n", hPreview, debugstr_w(szDialogName));
preview = msihandle2msiinfo( hPreview, MSIHANDLETYPE_PREVIEW );
if( !preview )
return ERROR_INVALID_HANDLE;
r = MSI_PreviewDialogW( preview, szDialogName );
msiobj_release( &preview->hdr );
return r;
}
UINT WINAPI MsiPreviewDialogA( MSIHANDLE hPreview, LPCSTR szDialogName )
{
UINT r;
LPWSTR strW = NULL;
TRACE("%ld %s\n", hPreview, debugstr_a(szDialogName));
if( szDialogName )
{
strW = strdupAtoW( szDialogName );
if( !strW )
return ERROR_OUTOFMEMORY;
}
r = MsiPreviewDialogW( hPreview, strW );
msi_free( strW );
return r;
}
UINT WINAPI MsiPreviewBillboardW( MSIHANDLE hPreview, LPCWSTR szControlName,
LPCWSTR szBillboard)
{
FIXME("%ld %s %s\n", hPreview, debugstr_w(szControlName),
debugstr_w(szBillboard));
return ERROR_CALL_NOT_IMPLEMENTED;
}
UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName,
LPCSTR szBillboard)
{
FIXME("%ld %s %s\n", hPreview, debugstr_a(szControlName),
debugstr_a(szBillboard));
return ERROR_CALL_NOT_IMPLEMENTED;
}
--- NEW FILE: query.h ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __WINE_MSI_QUERY_H
#define __WINE_MSI_QUERY_H
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "objbase.h"
#include "objidl.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "msipriv.h"
#include "wine-lgpl/list.h"
#define OP_EQ 1
#define OP_AND 2
#define OP_OR 3
#define OP_GT 4
#define OP_LT 5
#define OP_LE 6
#define OP_GE 7
#define OP_NE 8
#define OP_ISNULL 9
#define OP_NOTNULL 10
#define EXPR_COMPLEX 1
#define EXPR_COLUMN 2
#define EXPR_COL_NUMBER 3
#define EXPR_IVAL 4
#define EXPR_SVAL 5
#define EXPR_UVAL 6
#define EXPR_STRCMP 7
#define EXPR_WILDCARD 9
#define EXPR_COL_NUMBER_STRING 10
#define EXPR_COL_NUMBER32 11
#define EXPR_UNARY 12
struct sql_str {
LPCWSTR data;
INT len;
};
typedef struct _column_info
{
LPCWSTR table;
LPCWSTR column;
UINT type;
BOOL temporary;
struct expr *val;
struct _column_info *next;
} column_info;
struct complex_expr
{
UINT op;
struct expr *left;
struct expr *right;
};
struct expr
{
int type;
union
{
struct complex_expr expr;
INT ival;
UINT uval;
LPCWSTR sval;
LPCWSTR column;
UINT col_number;
} u;
};
UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview,
struct list *mem );
UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view );
UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
const column_info *columns );
UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
column_info *columns );
UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
struct expr *cond );
UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
column_info *col_info, BOOL hold );
UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
column_info *columns, column_info *values, BOOL temp );
UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
column_info *list, struct expr *expr );
UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
UINT JOIN_CreateView( MSIDATABASE *db, MSIVIEW **view,
LPCWSTR left, LPCWSTR right );
UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold );
UINT STREAMS_CreateView( MSIDATABASE *db, MSIVIEW **view );
int sqliteGetToken(const WCHAR *z, int *tokenType);
MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec );
UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
BOOL persistent, MSITABLE **table_ret);
#endif /* __WINE_MSI_QUERY_H */
--- NEW FILE: record.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "msipriv.h"
#include "objidl.h"
#include "winnls.h"
#include "ole2.h"
#include "winreg.h"
#include "shlwapi.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
#define MSIFIELD_NULL 0
#define MSIFIELD_INT 1
#define MSIFIELD_WSTR 3
#define MSIFIELD_STREAM 4
static void MSI_FreeField( MSIFIELD *field )
{
switch( field->type )
{
case MSIFIELD_NULL:
case MSIFIELD_INT:
break;
case MSIFIELD_WSTR:
msi_free( field->u.szwVal);
break;
case MSIFIELD_STREAM:
IStream_Release( field->u.stream );
break;
default:
ERR("Invalid field type %d\n", field->type);
}
}
static void MSI_CloseRecord( MSIOBJECTHDR *arg )
{
MSIRECORD *rec = (MSIRECORD *) arg;
UINT i;
for( i=0; i<=rec->count; i++ )
MSI_FreeField( &rec->fields[i] );
}
MSIRECORD *MSI_CreateRecord( unsigned int cParams )
{
MSIRECORD *rec;
UINT len;
TRACE("%d\n", cParams);
if( cParams>65535 )
return NULL;
len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
if( rec )
rec->count = cParams;
return rec;
}
MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
{
MSIRECORD *rec;
MSIHANDLE ret = 0;
TRACE("%d\n", cParams);
rec = MSI_CreateRecord( cParams );
if( rec )
{
ret = alloc_msihandle( &rec->hdr );
msiobj_release( &rec->hdr );
}
return ret;
}
unsigned int MSI_RecordGetFieldCount( const MSIRECORD *rec )
{
return rec->count;
}
unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld\n", handle );
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return -1;
msiobj_lock( &rec->hdr );
ret = MSI_RecordGetFieldCount( rec );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
static BOOL string2intW( LPCWSTR str, int *out )
{
int x = 0;
LPCWSTR p = str;
if( *p == '-' ) /* skip the minus sign */
p++;
while ( *p )
{
if( (*p < '0') || (*p > '9') )
return FALSE;
x *= 10;
x += (*p - '0');
p++;
}
if( str[0] == '-' ) /* check if it's negative */
x = -x;
*out = x;
return TRUE;
}
UINT MSI_RecordCopyField( MSIRECORD *in_rec, unsigned int in_n,
MSIRECORD *out_rec, unsigned int out_n )
{
UINT r = ERROR_SUCCESS;
msiobj_lock( &in_rec->hdr );
if ( in_n > in_rec->count || out_n > out_rec->count )
r = ERROR_FUNCTION_FAILED;
else if ( in_rec != out_rec || in_n != out_n )
{
LPWSTR str;
MSIFIELD *in, *out;
in = &in_rec->fields[in_n];
out = &out_rec->fields[out_n];
switch ( in->type )
{
case MSIFIELD_NULL:
break;
case MSIFIELD_INT:
out->u.iVal = in->u.iVal;
break;
case MSIFIELD_WSTR:
str = strdupW( in->u.szwVal );
if ( !str )
r = ERROR_OUTOFMEMORY;
else
out->u.szwVal = str;
break;
case MSIFIELD_STREAM:
IStream_AddRef( in->u.stream );
out->u.stream = in->u.stream;
break;
default:
ERR("invalid field type %d\n", in->type);
}
if (r == ERROR_SUCCESS)
out->type = in->type;
}
msiobj_unlock( &in_rec->hdr );
return r;
}
int MSI_RecordGetInteger( MSIRECORD *rec, unsigned int iField)
{
int ret = 0;
TRACE("%p %d\n", rec, iField );
if( iField > rec->count )
return MSI_NULL_INTEGER;
switch( rec->fields[iField].type )
{
case MSIFIELD_INT:
return rec->fields[iField].u.iVal;
case MSIFIELD_WSTR:
if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
return ret;
return MSI_NULL_INTEGER;
default:
break;
}
return MSI_NULL_INTEGER;
}
int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d\n", handle, iField );
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return MSI_NULL_INTEGER;
msiobj_lock( &rec->hdr );
ret = MSI_RecordGetInteger( rec, iField );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
{
MSIRECORD *rec;
UINT i;
TRACE("%ld\n", handle );
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
for( i=0; i<=rec->count; i++)
{
MSI_FreeField( &rec->fields[i] );
rec->fields[i].type = MSIFIELD_NULL;
rec->fields[i].u.iVal = 0;
}
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ERROR_SUCCESS;
}
UINT MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal )
{
TRACE("%p %u %d\n", rec, iField, iVal);
if( iField > rec->count )
return ERROR_INVALID_PARAMETER;
MSI_FreeField( &rec->fields[iField] );
rec->fields[iField].type = MSIFIELD_INT;
rec->fields[iField].u.iVal = iVal;
return ERROR_SUCCESS;
}
UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal )
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %u %d\n", handle, iField, iVal);
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordSetInteger( rec, iField, iVal );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int iField )
{
BOOL r = TRUE;
TRACE("%p %d\n", rec, iField );
r = ( iField > rec->count ) ||
( rec->fields[iField].type == MSIFIELD_NULL );
return r;
}
BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField )
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d\n", handle, iField );
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return 0;
msiobj_lock( &rec->hdr );
ret = MSI_RecordIsNull( rec, iField );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField,
LPSTR szValue, DWORD *pcchValue)
{
UINT len=0, ret;
CHAR buffer[16];
TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
if( iField > rec->count )
return ERROR_INVALID_PARAMETER;
ret = ERROR_SUCCESS;
switch( rec->fields[iField].type )
{
case MSIFIELD_INT:
wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
len = lstrlenA( buffer );
if (szValue)
lstrcpynA(szValue, buffer, *pcchValue);
break;
case MSIFIELD_WSTR:
len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
NULL, 0 , NULL, NULL);
WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
szValue, *pcchValue, NULL, NULL);
if( szValue && *pcchValue && len>*pcchValue )
szValue[*pcchValue-1] = 0;
if( len )
len--;
break;
case MSIFIELD_NULL:
if( *pcchValue > 0 )
szValue[0] = 0;
break;
default:
ret = ERROR_INVALID_PARAMETER;
break;
}
if( szValue && *pcchValue <= len )
ret = ERROR_MORE_DATA;
*pcchValue = len;
return ret;
}
UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField,
LPSTR szValue, DWORD *pcchValue)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, unsigned int iField )
{
if( iField > rec->count )
return NULL;
if( rec->fields[iField].type != MSIFIELD_WSTR )
return NULL;
return rec->fields[iField].u.szwVal;
}
UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField,
LPWSTR szValue, DWORD *pcchValue)
{
UINT len=0, ret;
WCHAR buffer[16];
static const WCHAR szFormat[] = { '%','d',0 };
TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
if( iField > rec->count )
return ERROR_INVALID_PARAMETER;
ret = ERROR_SUCCESS;
switch( rec->fields[iField].type )
{
case MSIFIELD_INT:
wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
len = lstrlenW( buffer );
if (szValue)
lstrcpynW(szValue, buffer, *pcchValue);
break;
case MSIFIELD_WSTR:
len = lstrlenW( rec->fields[iField].u.szwVal );
if (szValue)
lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
break;
case MSIFIELD_NULL:
len = 1;
if( szValue && *pcchValue > 0 )
szValue[0] = 0;
default:
break;
}
if( szValue && *pcchValue <= len )
ret = ERROR_MORE_DATA;
*pcchValue = len;
return ret;
}
UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
LPWSTR szValue, DWORD *pcchValue)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
static UINT msi_get_stream_size( IStream *stm )
{
STATSTG stat;
HRESULT r;
r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
if( FAILED(r) )
return 0;
return stat.cbSize.QuadPart;
}
UINT MSI_RecordDataSize(MSIRECORD *rec, unsigned int iField)
{
TRACE("%p %d\n", rec, iField);
if( iField > rec->count )
return 0;
switch( rec->fields[iField].type )
{
case MSIFIELD_INT:
return sizeof (INT);
case MSIFIELD_WSTR:
return lstrlenW( rec->fields[iField].u.szwVal );
case MSIFIELD_NULL:
break;
case MSIFIELD_STREAM:
return msi_get_stream_size( rec->fields[iField].u.stream );
}
return 0;
}
UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, unsigned int iField)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d\n", handle, iField);
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return 0;
msiobj_lock( &rec->hdr );
ret = MSI_RecordDataSize( rec, iField);
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
{
LPWSTR str;
TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
if( iField > rec->count )
return ERROR_INVALID_FIELD;
MSI_FreeField( &rec->fields[iField] );
if( szValue && szValue[0] )
{
str = strdupAtoW( szValue );
rec->fields[iField].type = MSIFIELD_WSTR;
rec->fields[iField].u.szwVal = str;
}
else
{
rec->fields[iField].type = MSIFIELD_NULL;
rec->fields[iField].u.szwVal = NULL;
}
return 0;
}
UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordSetStringA( rec, iField, szValue );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
{
LPWSTR str;
TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
if( iField > rec->count )
return ERROR_INVALID_FIELD;
MSI_FreeField( &rec->fields[iField] );
if( szValue && szValue[0] )
{
str = strdupW( szValue );
rec->fields[iField].type = MSIFIELD_WSTR;
rec->fields[iField].u.szwVal = str;
}
else
{
rec->fields[iField].type = MSIFIELD_NULL;
rec->fields[iField].u.szwVal = NULL;
}
return 0;
}
UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordSetStringW( rec, iField, szValue );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
/* read the data in a file into an IStream */
static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
{
DWORD sz, szHighWord = 0, read;
HANDLE handle;
HGLOBAL hGlob = 0;
HRESULT hr;
ULARGE_INTEGER ulSize;
TRACE("reading %s\n", debugstr_w(szFile));
/* read the file into memory */
handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if( handle == INVALID_HANDLE_VALUE )
return GetLastError();
sz = GetFileSize(handle, &szHighWord);
if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
{
hGlob = GlobalAlloc(GMEM_FIXED, sz);
if( hGlob )
{
BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
if( !r )
{
GlobalFree(hGlob);
hGlob = 0;
}
}
}
CloseHandle(handle);
if( !hGlob )
return ERROR_FUNCTION_FAILED;
/* make a stream out of it, and set the correct file size */
hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
if( FAILED( hr ) )
{
GlobalFree(hGlob);
return ERROR_FUNCTION_FAILED;
}
/* set the correct size - CreateStreamOnHGlobal screws it up */
ulSize.QuadPart = sz;
IStream_SetSize(*pstm, ulSize);
TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
return ERROR_SUCCESS;
}
UINT MSI_RecordSetStream(MSIRECORD *rec, unsigned int iField, IStream *stream)
{
if ( (iField == 0) || (iField > rec->count) )
return ERROR_INVALID_PARAMETER;
MSI_FreeField( &rec->fields[iField] );
rec->fields[iField].type = MSIFIELD_STREAM;
rec->fields[iField].u.stream = stream;
return ERROR_SUCCESS;
}
UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename)
{
IStream *stm = NULL;
HRESULT r;
if( (iField == 0) || (iField > rec->count) )
return ERROR_INVALID_PARAMETER;
/* no filename means we should seek back to the start of the stream */
if( !szFilename )
{
LARGE_INTEGER ofs;
ULARGE_INTEGER cur;
if( rec->fields[iField].type != MSIFIELD_STREAM )
return ERROR_INVALID_FIELD;
stm = rec->fields[iField].u.stream;
if( !stm )
return ERROR_INVALID_FIELD;
ofs.QuadPart = 0;
r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
if( FAILED( r ) )
return ERROR_FUNCTION_FAILED;
}
else
{
/* read the file into a stream and save the stream in the record */
r = RECORD_StreamFromFile(szFilename, &stm);
if( r != ERROR_SUCCESS )
return r;
/* if all's good, store it in the record */
MSI_RecordSetStream(rec, iField, stm);
}
return ERROR_SUCCESS;
}
UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
{
LPWSTR wstr = NULL;
UINT ret;
TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
if( szFilename )
{
wstr = strdupAtoW( szFilename );
if( !wstr )
return ERROR_OUTOFMEMORY;
}
ret = MsiRecordSetStreamW(hRecord, iField, wstr);
msi_free(wstr);
return ret;
}
UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename));
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
{
ULONG count;
HRESULT r;
IStream *stm;
TRACE("%p %d %p %p\n", rec, iField, buf, sz);
if( !sz )
return ERROR_INVALID_PARAMETER;
if( iField > rec->count)
return ERROR_INVALID_PARAMETER;
if( rec->fields[iField].type != MSIFIELD_STREAM )
return ERROR_INVALID_DATATYPE;
stm = rec->fields[iField].u.stream;
if( !stm )
return ERROR_INVALID_PARAMETER;
/* if there's no buffer pointer, calculate the length to the end */
if( !buf )
{
LARGE_INTEGER ofs;
ULARGE_INTEGER end, cur;
ofs.QuadPart = cur.QuadPart = 0;
end.QuadPart = 0;
r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
ofs.QuadPart = cur.QuadPart;
IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
*sz = end.QuadPart - cur.QuadPart;
return ERROR_SUCCESS;
}
/* read the data */
count = 0;
r = IStream_Read( stm, buf, *sz, &count );
if( FAILED( r ) )
{
*sz = 0;
return ERROR_FUNCTION_FAILED;
}
*sz = count;
return ERROR_SUCCESS;
}
UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
{
MSIRECORD *rec;
UINT ret;
TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
if( !rec )
return ERROR_INVALID_HANDLE;
msiobj_lock( &rec->hdr );
ret = MSI_RecordReadStream( rec, iField, buf, sz );
msiobj_unlock( &rec->hdr );
msiobj_release( &rec->hdr );
return ret;
}
UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
{
TRACE("%p %d %p\n", rec, iField, stm);
if( iField > rec->count )
return ERROR_INVALID_FIELD;
MSI_FreeField( &rec->fields[iField] );
rec->fields[iField].type = MSIFIELD_STREAM;
rec->fields[iField].u.stream = stm;
IStream_AddRef( stm );
return ERROR_SUCCESS;
}
UINT MSI_RecordGetIStream( MSIRECORD *rec, unsigned int iField, IStream **pstm)
{
TRACE("%p %d %p\n", rec, iField, pstm);
if( iField > rec->count )
return ERROR_INVALID_FIELD;
if( rec->fields[iField].type != MSIFIELD_STREAM )
return ERROR_INVALID_FIELD;
*pstm = rec->fields[iField].u.stream;
IStream_AddRef( *pstm );
return ERROR_SUCCESS;
}
static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
{
ULARGE_INTEGER size;
LARGE_INTEGER pos;
IStream *out;
DWORD stgm;
HRESULT r;
stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
r = SHCreateStreamOnFileW( name, stgm, &out );
if( FAILED( r ) )
return ERROR_FUNCTION_FAILED;
pos.QuadPart = 0;
r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
if( FAILED( r ) )
goto end;
pos.QuadPart = 0;
r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
if( FAILED( r ) )
goto end;
r = IStream_CopyTo( stm, out, size, NULL, NULL );
end:
IStream_Release( out );
if( FAILED( r ) )
return ERROR_FUNCTION_FAILED;
return ERROR_SUCCESS;
}
UINT MSI_RecordStreamToFile( MSIRECORD *rec, unsigned int iField, LPCWSTR name )
{
IStream *stm = NULL;
UINT r;
TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
msiobj_lock( &rec->hdr );
r = MSI_RecordGetIStream( rec, iField, &stm );
if( r == ERROR_SUCCESS )
{
r = msi_dump_stream_to_file( stm, name );
IStream_Release( stm );
}
msiobj_unlock( &rec->hdr );
return r;
}
--- NEW FILE: registry.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Mike McCormack for CodeWeavers
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
[...1349 lines suppressed...]
UINT WINAPI MsiEnumProductsExA( LPCSTR szProductCode, LPCSTR szUserSid,
DWORD dwContext, DWORD dwIndex, LPSTR szInstalledProductCode,
MSIINSTALLCONTEXT* pdwInstalledContext, LPSTR szSid, LPDWORD pcchSid)
{
FIXME("%s %s %d %d %p %p %p %p\n", debugstr_a(szProductCode), debugstr_a(szUserSid),
dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
szSid, pcchSid);
return ERROR_NO_MORE_ITEMS;
}
UINT WINAPI MsiEnumProductsExW( LPCWSTR szProductCode, LPCWSTR szUserSid,
DWORD dwContext, DWORD dwIndex, LPWSTR szInstalledProductCode,
MSIINSTALLCONTEXT* pdwInstalledContext, LPWSTR szSid, LPDWORD pcchSid)
{
FIXME("%s %s %d %d %p %p %p %p\n", debugstr_w(szProductCode), debugstr_w(szUserSid),
dwContext, dwIndex, szInstalledProductCode, pdwInstalledContext,
szSid, pcchSid);
return ERROR_NO_MORE_ITEMS;
}
--- NEW FILE: regsvr.c ---
/*
* self-registerable dll functions for msi.dll
*
* Copyright (C) 2004 Raphael Junqueira
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "config.h"
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winreg.h"
#include "winerror.h"
#include "ole2.h"
#include "olectl.h"
#include "oleauto.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "initguid.h"
#include "msipriv.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
/*
* Near the bottom of this file are the exported DllRegisterServer and
* DllUnregisterServer, which make all this worthwhile.
*/
/***********************************************************************
* interface for self-registering
*/
struct regsvr_interface {
IID const *iid; /* NULL for end of list */
LPCSTR name; /* can be NULL to omit */
IID const *base_iid; /* can be NULL to omit */
int num_methods; /* can be <0 to omit */
CLSID const *ps_clsid; /* can be NULL to omit */
CLSID const *ps_clsid32; /* can be NULL to omit */
};
static HRESULT register_interfaces(struct regsvr_interface const *list);
static HRESULT unregister_interfaces(struct regsvr_interface const *list);
/**
* @todo: maybe adding typelibs support here
* [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217
* @="{000C1092-0000-0000-C000-000000000046}"
*/
struct regsvr_coclass {
CLSID const *clsid; /* NULL for end of list */
LPCSTR name; /* can be NULL to omit */
LPCSTR iph32; /* can be NULL to omit */
LPCSTR ips; /* can be NULL to omit */
LPCSTR ips32; /* can be NULL to omit */
LPCSTR ips32_tmodel; /* can be NULL to omit, if apartment, iph32 must be set */
DWORD flags;
LPCSTR progid; /* can be NULL to omit */
LPCSTR viprogid; /* can be NULL to omit */
LPCSTR progid_extra; /* can be NULL to omit */
};
/* flags for regsvr_coclass.flags */
#define PROGID_CLSID 0x00000010
static HRESULT register_coclasses(struct regsvr_coclass const *list);
static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
/***********************************************************************
* static string constants
*/
static WCHAR const interface_keyname[10] = {
'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
static WCHAR const base_ifa_keyname[14] = {
'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
'e', 0 };
static WCHAR const num_methods_keyname[11] = {
'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
static WCHAR const ps_clsid_keyname[15] = {
'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
'i', 'd', 0 };
static WCHAR const ps_clsid32_keyname[17] = {
'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
'i', 'd', '3', '2', 0 };
static WCHAR const clsid_keyname[6] = {
'C', 'L', 'S', 'I', 'D', 0 };
static WCHAR const curver_keyname[7] = {
'C', 'u', 'r', 'V', 'e', 'r', 0 };
static WCHAR const iph32_keyname[] = {
'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
'3', '2', 0 };
static WCHAR const ips_keyname[13] = {
'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
0 };
static WCHAR const ips32_keyname[15] = {
'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
'3', '2', 0 };
static WCHAR const progid_keyname[7] = {
'P', 'r', 'o', 'g', 'I', 'D', 0 };
static WCHAR const viprogid_keyname[25] = {
'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
0 };
static char const tmodel_valuename[] = "ThreadingModel";
/***********************************************************************
* static helper functions
*/
static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
WCHAR const *value);
static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
char const *value);
static LONG register_progid(WCHAR const *clsid,
char const *progid, char const *curver_progid,
char const *name, char const *extra);
static LONG recursive_delete_key(HKEY key);
static LONG recursive_delete_keyA(HKEY base, char const *name);
static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
/***********************************************************************
* register_interfaces
*/
static HRESULT register_interfaces(struct regsvr_interface const *list) {
LONG res = ERROR_SUCCESS;
HKEY interface_key;
res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
if (res != ERROR_SUCCESS) goto error_return;
for (; res == ERROR_SUCCESS && list->iid; ++list) {
WCHAR buf[39];
HKEY iid_key;
StringFromGUID2(list->iid, buf, 39);
res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
if (res != ERROR_SUCCESS) goto error_close_interface_key;
if (list->name) {
res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
(CONST BYTE*)(list->name),
strlen(list->name) + 1);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
}
if (list->base_iid) {
res = register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
}
if (0 <= list->num_methods) {
static WCHAR const fmt[3] = { '%', 'd', 0 };
HKEY key;
res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &key, NULL);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
wsprintfW(buf, fmt, list->num_methods);
res = RegSetValueExW(key, NULL, 0, REG_SZ,
(CONST BYTE*)buf,
(lstrlenW(buf) + 1) * sizeof(WCHAR));
RegCloseKey(key);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
}
if (list->ps_clsid) {
res = register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
}
if (list->ps_clsid32) {
res = register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
if (res != ERROR_SUCCESS) goto error_close_iid_key;
}
error_close_iid_key:
RegCloseKey(iid_key);
}
error_close_interface_key:
RegCloseKey(interface_key);
error_return:
return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
}
/***********************************************************************
* unregister_interfaces
*/
static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
LONG res = ERROR_SUCCESS;
HKEY interface_key;
res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
KEY_READ | KEY_WRITE, &interface_key);
if (res == ERROR_FILE_NOT_FOUND) return S_OK;
if (res != ERROR_SUCCESS) goto error_return;
for (; res == ERROR_SUCCESS && list->iid; ++list) {
WCHAR buf[39];
StringFromGUID2(list->iid, buf, 39);
res = recursive_delete_keyW(interface_key, buf);
}
RegCloseKey(interface_key);
error_return:
return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
}
/***********************************************************************
* register_coclasses
*/
static HRESULT register_coclasses(struct regsvr_coclass const *list) {
LONG res = ERROR_SUCCESS;
HKEY coclass_key;
res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
if (res != ERROR_SUCCESS) goto error_return;
for (; res == ERROR_SUCCESS && list->clsid; ++list) {
WCHAR buf[39];
HKEY clsid_key;
StringFromGUID2(list->clsid, buf, 39);
res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
if (res != ERROR_SUCCESS) goto error_close_coclass_key;
if (list->name) {
res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
(CONST BYTE*)(list->name),
strlen(list->name) + 1);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
if (list->iph32) {
HKEY iph32_key;
res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL,
&iph32_key, NULL);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
(CONST BYTE*)list->iph32,
lstrlenA(list->iph32) + 1);
RegCloseKey(iph32_key);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
if (list->ips) {
res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
if (list->ips32) {
HKEY ips32_key;
res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL,
&ips32_key, NULL);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
(CONST BYTE*)list->ips32,
lstrlenA(list->ips32) + 1);
if (res == ERROR_SUCCESS && list->ips32_tmodel)
res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
(CONST BYTE*)list->ips32_tmodel,
strlen(list->ips32_tmodel) + 1);
RegCloseKey(ips32_key);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
if (list->progid) {
res = register_key_defvalueA(clsid_key, progid_keyname,
list->progid);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
res = register_progid(list->flags & PROGID_CLSID ? buf : NULL,
list->progid, NULL,
list->name, list->progid_extra);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
if (list->viprogid) {
res = register_key_defvalueA(clsid_key, viprogid_keyname,
list->viprogid);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
res = register_progid(list->flags & PROGID_CLSID ? buf : NULL,
list->viprogid, list->progid,
list->name, list->progid_extra);
if (res != ERROR_SUCCESS) goto error_close_clsid_key;
}
error_close_clsid_key:
RegCloseKey(clsid_key);
}
error_close_coclass_key:
RegCloseKey(coclass_key);
error_return:
return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
}
/***********************************************************************
* unregister_coclasses
*/
static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
LONG res = ERROR_SUCCESS;
HKEY coclass_key;
res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
KEY_READ | KEY_WRITE, &coclass_key);
if (res == ERROR_FILE_NOT_FOUND) return S_OK;
if (res != ERROR_SUCCESS) goto error_return;
for (; res == ERROR_SUCCESS && list->clsid; ++list) {
WCHAR buf[39];
StringFromGUID2(list->clsid, buf, 39);
res = recursive_delete_keyW(coclass_key, buf);
if (res != ERROR_SUCCESS) goto error_close_coclass_key;
if (list->progid) {
res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
if (res != ERROR_SUCCESS) goto error_close_coclass_key;
}
if (list->viprogid) {
res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
if (res != ERROR_SUCCESS) goto error_close_coclass_key;
}
}
error_close_coclass_key:
RegCloseKey(coclass_key);
error_return:
return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
}
/***********************************************************************
* regsvr_key_guid
*/
static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
WCHAR buf[39];
StringFromGUID2(guid, buf, 39);
return register_key_defvalueW(base, name, buf);
}
/***********************************************************************
* regsvr_key_defvalueW
*/
static LONG register_key_defvalueW(
HKEY base,
WCHAR const *name,
WCHAR const *value) {
LONG res;
HKEY key;
res = RegCreateKeyExW(base, name, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &key, NULL);
if (res != ERROR_SUCCESS) return res;
res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
(lstrlenW(value) + 1) * sizeof(WCHAR));
RegCloseKey(key);
return res;
}
/***********************************************************************
* regsvr_key_defvalueA
*/
static LONG register_key_defvalueA(
HKEY base,
WCHAR const *name,
char const *value) {
LONG res;
HKEY key;
res = RegCreateKeyExW(base, name, 0, NULL, 0,
KEY_READ | KEY_WRITE, NULL, &key, NULL);
if (res != ERROR_SUCCESS) return res;
res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
lstrlenA(value) + 1);
RegCloseKey(key);
return res;
}
/***********************************************************************
* regsvr_progid
*/
static LONG register_progid(
WCHAR const *clsid,
char const *progid,
char const *curver_progid,
char const *name,
char const *extra) {
LONG res;
HKEY progid_key;
res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
NULL, 0, KEY_READ | KEY_WRITE, NULL,
&progid_key, NULL);
if (res != ERROR_SUCCESS) return res;
if (name) {
res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
(CONST BYTE*)name, strlen(name) + 1);
if (res != ERROR_SUCCESS) goto error_close_progid_key;
}
if (clsid) {
res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
if (res != ERROR_SUCCESS) goto error_close_progid_key;
}
if (curver_progid) {
res = register_key_defvalueA(progid_key, curver_keyname,
curver_progid);
if (res != ERROR_SUCCESS) goto error_close_progid_key;
}
if (extra) {
HKEY extra_key;
res = RegCreateKeyExA(progid_key, extra, 0,
NULL, 0, KEY_READ | KEY_WRITE, NULL,
&extra_key, NULL);
if (res == ERROR_SUCCESS)
RegCloseKey(extra_key);
}
error_close_progid_key:
RegCloseKey(progid_key);
return res;
}
/***********************************************************************
* recursive_delete_key
*/
static LONG recursive_delete_key(HKEY key) {
LONG res;
WCHAR subkey_name[MAX_PATH];
DWORD cName;
HKEY subkey;
for (;;) {
cName = sizeof(subkey_name) / sizeof(WCHAR);
res = RegEnumKeyExW(key, 0, subkey_name, &cName,
NULL, NULL, NULL, NULL);
if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
res = ERROR_SUCCESS; /* presumably we're done enumerating */
break;
}
res = RegOpenKeyExW(key, subkey_name, 0,
KEY_READ | KEY_WRITE, &subkey);
if (res == ERROR_FILE_NOT_FOUND) continue;
if (res != ERROR_SUCCESS) break;
res = recursive_delete_key(subkey);
RegCloseKey(subkey);
if (res != ERROR_SUCCESS) break;
}
if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
return res;
}
/***********************************************************************
* recursive_delete_keyA
*/
static LONG recursive_delete_keyA(HKEY base, char const *name) {
LONG res;
HKEY key;
res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
if (res != ERROR_SUCCESS) return res;
res = recursive_delete_key(key);
RegCloseKey(key);
return res;
}
/***********************************************************************
* recursive_delete_keyW
*/
static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
LONG res;
HKEY key;
res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
if (res != ERROR_SUCCESS) return res;
res = recursive_delete_key(key);
RegCloseKey(key);
return res;
}
/***********************************************************************
* coclass list
*/
static struct regsvr_coclass const coclass_list[] = {
{
&CLSID_IMsiServer,
"Msi install server",
"ole32.dll",
NULL,
"msi.dll",
"Apartment",
PROGID_CLSID,
"IMsiServer",
NULL
},
{
&CLSID_IMsiServerMessage,
"Wine Installer Message RPC",
NULL,
NULL,
"msi.dll",
NULL,
PROGID_CLSID,
"WindowsInstaller.Message",
NULL
},
{
&CLSID_IMsiServerX1,
"Msi install server",
"ole32.dll",
NULL,
"msi.dll",
"Apartment",
0,
"WindowsInstaller.Installer",
NULL
},
{
&CLSID_IMsiServerX2,
"Msi install server",
"ole32.dll",
NULL,
"msi.dll",
"Apartment",
PROGID_CLSID,
"WindowsInstaller.Installer",
NULL
},
{
&CLSID_IMsiServerX3,
"Msi install server",
"ole32.dll",
NULL,
"msi.dll",
"Apartment",
0,
"WindowsInstaller.Installer",
NULL
},
{ NULL } /* list terminator */
};
/***********************************************************************
* interface list
*/
/*
* we should declare: (@see ole32/regsvr.c for examples)
[-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}]
[-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
*/
static struct regsvr_interface const interface_list[] = {
{ &CLSID_IMsiServer,
"IMsiServer",
NULL,
18,
NULL,
NULL },
{ NULL } /* list terminator */
};
static HRESULT register_msiexec(void)
{
static const WCHAR key[] = {
'S','o','f','t','w','a','r','e',
'\\','M','i','c','r','o','s','o','f','t',
'\\','W','i','n','d','o','w','s',
'\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
'\\','I','n','s','t','a','l','l','e','r',0 };
static const WCHAR val[] = {
'I','n','s','t','a','l','l','e','r','L','o','c','a','t','i','o','n',0 };
WCHAR path[MAX_PATH];
HKEY hkey;
LONG res;
INT len;
len = GetSystemDirectoryW(path, MAX_PATH);
if (!len || len > MAX_PATH)
return E_FAIL;
res = RegCreateKeyExW(HKEY_LOCAL_MACHINE, key, 0,
NULL, 0, KEY_READ | KEY_WRITE, NULL,
&hkey, NULL);
if (res != ERROR_SUCCESS)
return E_FAIL;
res = RegSetValueExW(hkey, val, 0, REG_SZ,
(BYTE*)path, (len + 1)*sizeof(WCHAR));
RegCloseKey(hkey);
return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
}
/***********************************************************************
* DllRegisterServer (MSI.@)
*/
HRESULT WINAPI DllRegisterServer(void)
{
LPWSTR path = NULL;
ITypeLib *tl;
HRESULT hr;
TRACE("\n");
hr = register_coclasses(coclass_list);
if (SUCCEEDED(hr))
hr = register_interfaces(interface_list);
if (SUCCEEDED(hr))
hr = register_msiexec();
tl = get_msi_typelib( &path );
if (tl)
{
hr = RegisterTypeLib( tl, path, NULL );
ITypeLib_Release( tl );
}
else
hr = E_FAIL;
return hr;
}
/***********************************************************************
* DllUnregisterServer (MSI.@)
*/
HRESULT WINAPI DllUnregisterServer(void)
{
HRESULT hr;
TRACE("\n");
hr = unregister_coclasses(coclass_list);
if (SUCCEEDED(hr))
hr = unregister_interfaces(interface_list);
return hr;
}
--- NEW FILE: script.c ---
/*
* Implementation of scripting for Microsoft Installer (msi.dll)
*
* Copyright 2007 Misha Koshelev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winuser.h"
#include "wine-lgpl/msidefs.h"
#include "msipriv.h"
#include "wine-lgpl/activscp.h"
#include "oleauto.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "msiserver.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0};
static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0};
static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0};
/*
* MsiActiveScriptSite - Our IActiveScriptSite implementation.
*/
typedef struct {
IActiveScriptSite lpVtbl;
IDispatch *pInstaller;
IDispatch *pSession;
LONG ref;
} MsiActiveScriptSite;
static const struct IActiveScriptSiteVtbl ASS_Vtbl;
static HRESULT create_ActiveScriptSite(IUnknown *pUnkOuter, LPVOID *ppObj)
{
MsiActiveScriptSite* object;
TRACE("(%p,%p)\n", pUnkOuter, ppObj);
if( pUnkOuter )
return CLASS_E_NOAGGREGATION;
object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MsiActiveScriptSite));
object->lpVtbl.lpVtbl = &ASS_Vtbl;
object->ref = 1;
object->pInstaller = NULL;
object->pSession = NULL;
*ppObj = object;
return S_OK;
}
/*
* Call a script.
*/
DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action)
{
HRESULT hr;
IActiveScript *pActiveScript = NULL;
IActiveScriptParse *pActiveScriptParse = NULL;
MsiActiveScriptSite *pActiveScriptSite = NULL;
IDispatch *pDispatch = NULL;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
DISPID dispid;
CLSID clsid;
VARIANT var;
/* Return success by default (if Windows Script not installed) - not native behavior. This
* should be here until we implement wine scripting. */
DWORD ret = ERROR_SUCCESS;
CoInitialize(NULL);
/* Create MsiActiveScriptSite object */
hr = create_ActiveScriptSite(NULL, (void **)&pActiveScriptSite);
if (hr != S_OK) goto done;
/* Create an installer object */
hr = create_msiserver(NULL, (LPVOID *)&pActiveScriptSite->pInstaller);
if (hr != S_OK) goto done;
IUnknown_AddRef((IUnknown *)pActiveScriptSite->pInstaller);
/* Create a session object */
hr = create_session(hPackage, pActiveScriptSite->pInstaller, &pActiveScriptSite->pSession);
if (hr != S_OK) goto done;
IUnknown_AddRef((IUnknown *)pActiveScriptSite->pSession);
/* Create the scripting engine */
if ((type & 7) == msidbCustomActionTypeJScript)
hr = CLSIDFromProgID(szJScript, &clsid);
else if ((type & 7) == msidbCustomActionTypeVBScript)
hr = CLSIDFromProgID(szVBScript, &clsid);
else {
ERR("Unknown script type %d\n", type);
goto done;
}
if (FAILED(hr)) {
ERR("Could not find CLSID for Windows Script\n");
goto done;
}
hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript);
if (FAILED(hr)) {
ERR("Could not instantiate class for Windows Script\n");
goto done;
}
/* If we got this far, Windows Script is installed, so don't return success by default anymore */
ret = ERROR_INSTALL_FAILURE;
/* Get the IActiveScriptParse engine interface */
hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse);
if (FAILED(hr)) goto done;
/* Give our host to the engine */
hr = IActiveScript_SetScriptSite(pActiveScript, (IActiveScriptSite *)pActiveScriptSite);
if (FAILED(hr)) goto done;
/* Initialize the script engine */
hr = IActiveScriptParse_InitNew(pActiveScriptParse);
if (FAILED(hr)) goto done;
/* Add the session object */
hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_ISVISIBLE);
/* Pass the script to the engine */
hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL);
if (FAILED(hr)) goto done;
/* Start processing the script */
hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED);
if (FAILED(hr)) goto done;
/* Call a function if necessary through the IDispatch interface */
if (function != NULL && strlenW(function) > 0) {
TRACE("Calling function %s\n", debugstr_w(function));
hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch);
if (FAILED(hr)) goto done;
hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) goto done;
hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL);
if (FAILED(hr)) goto done;
/* Check return value, if it's not IDOK we failed */
hr = VariantChangeType(&var, &var, 0, VT_I4);
if (FAILED(hr)) goto done;
if (V_I4(&var) == IDOK)
ret = ERROR_SUCCESS;
else ret = ERROR_INSTALL_FAILURE;
VariantClear(&var);
} else {
/* If no function to be called, MSI behavior is to succeed */
ret = ERROR_SUCCESS;
}
done:
/* Free everything that needs to be freed */
if (pDispatch) IDispatch_Release(pDispatch);
if (pActiveScript) IActiveScriptSite_Release(pActiveScript);
if (pActiveScriptSite &&
pActiveScriptSite->pSession) IUnknown_Release((IUnknown *)pActiveScriptSite->pSession);
if (pActiveScriptSite &&
pActiveScriptSite->pInstaller) IUnknown_Release((IUnknown *)pActiveScriptSite->pInstaller);
if (pActiveScriptSite) IUnknown_Release((IUnknown *)pActiveScriptSite);
CoUninitialize(); /* must call even if CoInitialize failed */
return ret;
}
/*
* MsiActiveScriptSite
*/
/*** IUnknown methods ***/
static HRESULT WINAPI MsiActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** ppvObject)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IActiveScriptSite))
{
IClassFactory_AddRef(iface);
*ppvObject = This;
return S_OK;
}
TRACE("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
return E_NOINTERFACE;
}
static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)\n", iface, This);
return InterlockedIncrement(&This->ref);
}
static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p/%p)\n", iface, This);
if (!ref)
HeapFree(GetProcessHeap(), 0, This);
return ref;
}
/*** IActiveScriptSite methods **/
static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)->(%p)\n", This, iface, plcid);
return E_NOTIMPL; /* Script will use system-defined locale */
}
static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)->(%p,%d,%p,%p)\n", This, iface, pstrName, dwReturnMask, ppiunkItem, ppti);
/* Determine the kind of pointer that is requested, and make sure placeholder is valid */
if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
if (!ppti) return E_INVALIDARG;
*ppti = NULL;
}
if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
if (!ppiunkItem) return E_INVALIDARG;
*ppiunkItem = NULL;
}
/* Are we looking for the session object? */
if (!strcmpW(szSession, pstrName)) {
if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
return load_type_info(This->pSession, ppti, &DIID_Session, 0);
else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
IDispatch_QueryInterface(This->pSession, &IID_IUnknown, (void **)ppiunkItem);
return S_OK;
}
}
return TYPE_E_ELEMENTNOTFOUND;
}
static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)->(%p)\n", This, iface, pbstrVersion);
return E_NOTIMPL;
}
static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)->(%p,%p)\n", This, iface, pvarResult, pexcepinfo);
return S_OK;
}
static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState)
{
switch (ssScriptState) {
case SCRIPTSTATE_UNINITIALIZED:
TRACE("State: Uninitialized.\n");
break;
case SCRIPTSTATE_INITIALIZED:
TRACE("State: Initialized.\n");
break;
case SCRIPTSTATE_STARTED:
TRACE("State: Started.\n");
break;
case SCRIPTSTATE_CONNECTED:
TRACE("State: Connected.\n");
break;
case SCRIPTSTATE_DISCONNECTED:
TRACE("State: Disconnected.\n");
break;
case SCRIPTSTATE_CLOSED:
TRACE("State: Closed.\n");
break;
default:
ERR("Unknown State: %d\n", ssScriptState);
break;
}
return S_OK;
}
static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
EXCEPINFO exception;
HRESULT hr;
TRACE("(%p/%p)->(%p)\n", This, iface, pscripterror);
hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception);
if (SUCCEEDED(hr))
ERR("script error: %s\n", debugstr_w(exception.bstrDescription));
return S_OK;
}
static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)\n", This, iface);
return S_OK;
}
static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface)
{
MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
TRACE("(%p/%p)\n", This, iface);
return S_OK;
}
static const struct IActiveScriptSiteVtbl ASS_Vtbl =
{
MsiActiveScriptSite_QueryInterface,
MsiActiveScriptSite_AddRef,
MsiActiveScriptSite_Release,
MsiActiveScriptSite_GetLCID,
MsiActiveScriptSite_GetItemInfo,
MsiActiveScriptSite_GetDocVersionString,
MsiActiveScriptSite_OnScriptTerminate,
MsiActiveScriptSite_OnStateChange,
MsiActiveScriptSite_OnScriptError,
MsiActiveScriptSite_OnEnterScript,
MsiActiveScriptSite_OnLeaveScript
};
--- NEW FILE: select.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSISELECTVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
UINT num_cols;
UINT max_cols;
UINT cols[1];
} MSISELECTVIEW;
static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %d %d %p\n", sv, row, col, val );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
if( (col==0) || (col>sv->num_cols) )
return ERROR_FUNCTION_FAILED;
col = sv->cols[ col - 1 ];
return sv->table->ops->fetch_int( sv->table, row, col, val );
}
static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %d %d %p\n", sv, row, col, stm );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
if( (col==0) || (col>sv->num_cols) )
return ERROR_FUNCTION_FAILED;
col = sv->cols[ col - 1 ];
return sv->table->ops->fetch_stream( sv->table, row, col, stm );
}
static UINT SELECT_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
UINT i, expanded_mask = 0, r = ERROR_SUCCESS, col_count = 0;
MSIRECORD *expanded;
TRACE("%p %d %p %08x\n", sv, row, rec, mask );
if ( !sv->table )
return ERROR_FUNCTION_FAILED;
/* test if any of the mask bits are invalid */
if ( mask >= (1<<sv->num_cols) )
return ERROR_INVALID_PARAMETER;
/* find the number of columns in the table below */
r = sv->table->ops->get_dimensions( sv->table, NULL, &col_count );
if( r )
return r;
/* expand the record to the right size for the underlying table */
expanded = MSI_CreateRecord( col_count );
if ( !expanded )
return ERROR_FUNCTION_FAILED;
/* move the right fields across */
for ( i=0; i<sv->num_cols; i++ )
{
r = MSI_RecordCopyField( rec, i+1, expanded, sv->cols[ i ] );
if (r != ERROR_SUCCESS)
break;
expanded_mask |= (1<<(sv->cols[i]-1));
}
/* set the row in the underlying table */
if (r == ERROR_SUCCESS)
r = sv->table->ops->set_row( sv->table, row, expanded, expanded_mask );
msiobj_release( &expanded->hdr );
return r;
}
static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, BOOL temporary )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
UINT i, table_cols, r;
MSIRECORD *outrec;
TRACE("%p %p\n", sv, record );
if ( !sv->table )
return ERROR_FUNCTION_FAILED;
/* rearrange the record to suit the table */
r = sv->table->ops->get_dimensions( sv->table, NULL, &table_cols );
if (r != ERROR_SUCCESS)
return r;
outrec = MSI_CreateRecord( table_cols + 1 );
for (i=0; i<sv->num_cols; i++)
{
r = MSI_RecordCopyField( record, i+1, outrec, sv->cols[i] );
if (r != ERROR_SUCCESS)
goto fail;
}
r = sv->table->ops->insert_row( sv->table, outrec, temporary );
fail:
msiobj_release( &outrec->hdr );
return r;
}
static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %p\n", sv, record);
if( !sv->table )
return ERROR_FUNCTION_FAILED;
return sv->table->ops->execute( sv->table, record );
}
static UINT SELECT_close( struct tagMSIVIEW *view )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p\n", sv );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
return sv->table->ops->close( sv->table );
}
static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %p %p\n", sv, rows, cols );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
if( cols )
*cols = sv->num_cols;
return sv->table->ops->get_dimensions( sv->table, rows, NULL );
}
static UINT SELECT_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %d %p %p\n", sv, n, name, type );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
if( (n==0) || (n>sv->num_cols) )
return ERROR_FUNCTION_FAILED;
n = sv->cols[ n - 1 ];
return sv->table->ops->get_column_info( sv->table, n, name, type );
}
static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p %d %p\n", sv, eModifyMode, rec );
if( !sv->table )
return ERROR_FUNCTION_FAILED;
return sv->table->ops->modify( sv->table, eModifyMode, rec );
}
static UINT SELECT_delete( struct tagMSIVIEW *view )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p\n", sv );
if( sv->table )
sv->table->ops->delete( sv->table );
sv->table = NULL;
msi_free( sv );
return ERROR_SUCCESS;
}
static UINT SELECT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
if( !sv->table )
return ERROR_FUNCTION_FAILED;
if( (col==0) || (col>sv->num_cols) )
return ERROR_FUNCTION_FAILED;
col = sv->cols[ col - 1 ];
return sv->table->ops->find_matching_rows( sv->table, col, val, row, handle );
}
static const MSIVIEWOPS select_ops =
{
SELECT_fetch_int,
SELECT_fetch_stream,
SELECT_set_row,
SELECT_insert_row,
NULL,
SELECT_execute,
SELECT_close,
SELECT_get_dimensions,
SELECT_get_column_info,
SELECT_modify,
SELECT_delete,
SELECT_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name )
{
UINT r, n=0;
MSIVIEW *table;
TRACE("%p adding %s\n", sv, debugstr_w( name ) );
if( sv->view.ops != &select_ops )
return ERROR_FUNCTION_FAILED;
table = sv->table;
if( !table )
return ERROR_FUNCTION_FAILED;
if( !table->ops->get_dimensions )
return ERROR_FUNCTION_FAILED;
if( !table->ops->get_column_info )
return ERROR_FUNCTION_FAILED;
if( sv->num_cols >= sv->max_cols )
return ERROR_FUNCTION_FAILED;
r = VIEW_find_column( table, name, &n );
if( r != ERROR_SUCCESS )
return r;
sv->cols[sv->num_cols] = n;
TRACE("Translating column %s from %d -> %d\n",
debugstr_w( name ), sv->num_cols, n);
sv->num_cols++;
return ERROR_SUCCESS;
}
static int select_count_columns( const column_info *col )
{
int n;
for (n = 0; col; col = col->next)
n++;
return n;
}
UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
const column_info *columns )
{
MSISELECTVIEW *sv = NULL;
UINT count = 0, r = ERROR_SUCCESS;
TRACE("%p\n", sv );
count = select_count_columns( columns );
sv = msi_alloc_zero( sizeof *sv + count*sizeof (UINT) );
if( !sv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
sv->view.ops = &select_ops;
sv->db = db;
sv->table = table;
sv->num_cols = 0;
sv->max_cols = count;
while( columns )
{
r = SELECT_AddColumn( sv, columns->column );
if( r )
break;
columns = columns->next;
}
if( r == ERROR_SUCCESS )
*view = &sv->view;
else
msi_free( sv );
return r;
}
--- NEW FILE: source.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "shlwapi.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "msipriv.h"
#include "wincrypt.h"
#include "winver.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "wine-lgpl/sddl.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
/*
* These apis are defined in MSI 3.0
*/
typedef struct tagMediaInfo
{
LPWSTR path;
WCHAR szIndex[10];
WCHAR type;
} media_info;
static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, DWORD dwOptions, BOOL user, BOOL create)
{
HKEY rootkey = 0;
UINT rc;
static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
if (user)
{
if (dwOptions == MSICODE_PATCH)
rc = MSIREG_OpenUserPatchesKey(szProduct, &rootkey, create);
else
rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create);
}
else
{
if (dwOptions == MSICODE_PATCH)
rc = MSIREG_OpenPatchesKey(szProduct, &rootkey, create);
else
rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create);
}
if (rc)
{
if (dwOptions == MSICODE_PATCH)
return ERROR_UNKNOWN_PATCH;
else
return ERROR_UNKNOWN_PRODUCT;
}
if (create)
rc = RegCreateKeyW(rootkey, szSourceList, key);
else
{
rc = RegOpenKeyW(rootkey,szSourceList, key);
if (rc != ERROR_SUCCESS)
rc = ERROR_BAD_CONFIGURATION;
}
return rc;
}
static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
{
UINT rc;
static const WCHAR media[] = {'M','e','d','i','a',0};
if (create)
rc = RegCreateKeyW(rootkey, media, key);
else
rc = RegOpenKeyW(rootkey,media, key);
return rc;
}
static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
{
UINT rc;
static const WCHAR net[] = {'N','e','t',0};
if (create)
rc = RegCreateKeyW(rootkey, net, key);
else
rc = RegOpenKeyW(rootkey, net, key);
return rc;
}
static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
{
UINT rc;
static const WCHAR URL[] = {'U','R','L',0};
if (create)
rc = RegCreateKeyW(rootkey, URL, key);
else
rc = RegOpenKeyW(rootkey, URL, key);
return rc;
}
static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss)
{
DWORD index = 0;
WCHAR szIndex[10];
DWORD size;
DWORD val_size;
LPWSTR val;
UINT rc = ERROR_SUCCESS;
while (rc == ERROR_SUCCESS)
{
val = NULL;
val_size = 0;
size = sizeof(szIndex)/sizeof(szIndex[0]);
rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size);
if (rc != ERROR_NO_MORE_ITEMS)
{
val = msi_alloc(val_size);
RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val,
&val_size);
if (lstrcmpiW(szSource,val)==0)
{
ss->path = val;
strcpyW(ss->szIndex,szIndex);
break;
}
else
strcpyW(ss->szIndex,szIndex);
msi_free(val);
index ++;
}
}
return rc;
}
/******************************************************************
* MsiSourceListGetInfoA (MSI.@)
*/
UINT WINAPI MsiSourceListGetInfoA( LPCSTR szProduct, LPCSTR szUserSid,
MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
LPCSTR szProperty, LPSTR szValue,
LPDWORD pcchValue)
{
UINT ret;
LPWSTR product = NULL;
LPWSTR usersid = NULL;
LPWSTR property = NULL;
LPWSTR value = NULL;
DWORD len = 0;
if (szValue && !pcchValue)
return ERROR_INVALID_PARAMETER;
if (szProduct) product = strdupAtoW(szProduct);
if (szUserSid) usersid = strdupAtoW(szUserSid);
if (szProperty) property = strdupAtoW(szProperty);
ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
property, NULL, &len);
if (ret != ERROR_SUCCESS)
goto done;
value = msi_alloc(++len * sizeof(WCHAR));
if (!value)
return ERROR_OUTOFMEMORY;
*value = '\0';
ret = MsiSourceListGetInfoW(product, usersid, dwContext, dwOptions,
property, value, &len);
if (ret != ERROR_SUCCESS)
goto done;
len = WideCharToMultiByte(CP_ACP, 0, value, -1, NULL, 0, NULL, NULL);
if (*pcchValue >= len)
WideCharToMultiByte(CP_ACP, 0, value, -1, szValue, len, NULL, NULL);
else if (szValue)
ret = ERROR_MORE_DATA;
*pcchValue = len - 1;
done:
msi_free(product);
msi_free(usersid);
msi_free(property);
msi_free(value);
return ret;
}
/******************************************************************
* MsiSourceListGetInfoW (MSI.@)
*/
UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
LPCWSTR szProperty, LPWSTR szValue,
LPDWORD pcchValue)
{
HKEY sourcekey;
UINT rc;
TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
if (!szProduct || !*szProduct)
return ERROR_INVALID_PARAMETER;
if (lstrlenW(szProduct) != GUID_SIZE - 1 ||
(szProduct[0] != '{' && szProduct[GUID_SIZE - 2] != '}'))
return ERROR_INVALID_PARAMETER;
if (szValue && !pcchValue)
return ERROR_INVALID_PARAMETER;
if (dwContext != MSIINSTALLCONTEXT_USERMANAGED &&
dwContext != MSIINSTALLCONTEXT_USERUNMANAGED &&
dwContext != MSIINSTALLCONTEXT_MACHINE)
return ERROR_INVALID_PARAMETER;
if (!szProperty)
return ERROR_INVALID_PARAMETER;
if (szUserSid)
FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
if (dwContext == MSIINSTALLCONTEXT_MACHINE)
rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, FALSE, FALSE);
else
rc = OpenSourceKey(szProduct, &sourcekey, dwOptions, TRUE, FALSE);
if (rc != ERROR_SUCCESS)
return rc;
if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
{
HKEY key;
rc = OpenMediaSubkey(sourcekey, &key, FALSE);
if (rc == ERROR_SUCCESS)
rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
0, 0, (LPBYTE)szValue, pcchValue);
if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
rc = ERROR_UNKNOWN_PROPERTY;
RegCloseKey(key);
}
else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
{
HKEY key;
rc = OpenMediaSubkey(sourcekey, &key, FALSE);
if (rc == ERROR_SUCCESS)
rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
(LPBYTE)szValue, pcchValue);
if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
rc = ERROR_UNKNOWN_PROPERTY;
RegCloseKey(key);
}
else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
{
LPWSTR buffer;
DWORD size = 0;
RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
NULL, &size);
if (size == 0)
rc = ERROR_UNKNOWN_PROPERTY;
else
{
LPWSTR ptr;
buffer = msi_alloc(size);
rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
0, 0, (LPBYTE)buffer,&size);
ptr = strchrW(buffer,';');
if (ptr) ptr = strchrW(ptr+1,';');
if (!ptr)
rc = ERROR_UNKNOWN_PROPERTY;
else
{
ptr ++;
lstrcpynW(szValue, ptr, *pcchValue);
if (lstrlenW(ptr) > *pcchValue)
{
*pcchValue = lstrlenW(ptr)+1;
rc = ERROR_MORE_DATA;
}
else
rc = ERROR_SUCCESS;
}
msi_free(buffer);
}
}
else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
{
LPWSTR buffer;
DWORD size = 0;
RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
NULL, &size);
if (size == 0)
rc = ERROR_UNKNOWN_PROPERTY;
else
{
buffer = msi_alloc(size);
rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
0, 0, (LPBYTE)buffer,&size);
if (*pcchValue < 1)
{
rc = ERROR_MORE_DATA;
*pcchValue = 1;
}
else
{
szValue[0] = buffer[0];
rc = ERROR_SUCCESS;
}
msi_free(buffer);
}
}
else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
{
*pcchValue = *pcchValue * sizeof(WCHAR);
rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0,
(LPBYTE)szValue, pcchValue);
if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
{
*pcchValue = 0;
rc = ERROR_SUCCESS;
}
else
{
if (*pcchValue)
*pcchValue = (*pcchValue - 1) / sizeof(WCHAR);
if (szValue)
szValue[*pcchValue] = '\0';
}
}
else
{
FIXME("Unknown property %s\n",debugstr_w(szProperty));
rc = ERROR_UNKNOWN_PROPERTY;
}
RegCloseKey(sourcekey);
return rc;
}
/******************************************************************
* MsiSourceListSetInfoW (MSI.@)
*/
UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
LPCWSTR szProperty, LPCWSTR szValue)
{
HKEY sourcekey;
UINT rc;
TRACE("%s %s %x %x %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid),
dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
if (!szProduct || lstrlenW(szProduct) > 39)
return ERROR_INVALID_PARAMETER;
if (dwOptions & MSICODE_PATCH)
{
FIXME("Unhandled options MSICODE_PATCH\n");
return ERROR_FUNCTION_FAILED;
}
if (szUserSid)
FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
if (dwContext == MSIINSTALLCONTEXT_MACHINE)
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, FALSE, TRUE);
else
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, TRUE, TRUE);
if (rc != ERROR_SUCCESS)
return ERROR_UNKNOWN_PRODUCT;
if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
{
HKEY key;
DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
rc = OpenMediaSubkey(sourcekey, &key, FALSE);
if (rc == ERROR_SUCCESS)
rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0,
REG_SZ, (const BYTE *)szValue, size);
if (rc != ERROR_SUCCESS)
rc = ERROR_UNKNOWN_PROPERTY;
RegCloseKey(key);
}
else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0)
{
HKEY key;
DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
rc = OpenMediaSubkey(sourcekey, &key, FALSE);
if (rc == ERROR_SUCCESS)
rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0,
REG_SZ, (const BYTE *)szValue, size);
if (rc != ERROR_SUCCESS)
rc = ERROR_UNKNOWN_PROPERTY;
RegCloseKey(key);
}
else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
{
LPWSTR buffer = NULL;
DWORD size;
WCHAR typechar = 'n';
static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0};
/* make sure the source is registered */
MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext,
dwOptions, szValue, 0);
if (dwOptions & MSISOURCETYPE_NETWORK)
typechar = 'n';
else if (dwOptions & MSISOURCETYPE_URL)
typechar = 'u';
else if (dwOptions & MSISOURCETYPE_MEDIA)
typechar = 'm';
else
ERR("Unknown source type! %x\n", dwOptions);
size = (lstrlenW(szValue)+5)*sizeof(WCHAR);
buffer = msi_alloc(size);
sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue);
rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0,
REG_EXPAND_SZ, (LPBYTE)buffer, size);
if (rc != ERROR_SUCCESS)
rc = ERROR_UNKNOWN_PROPERTY;
msi_free( buffer );
}
else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
{
DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
REG_SZ, (const BYTE *)szValue, size);
if (rc != ERROR_SUCCESS)
rc = ERROR_UNKNOWN_PROPERTY;
}
else
{
FIXME("Unknown property %s\n",debugstr_w(szProperty));
rc = ERROR_UNKNOWN_PROPERTY;
}
RegCloseKey(sourcekey);
return rc;
}
/******************************************************************
* MsiSourceListAddSourceW (MSI.@)
*/
UINT WINAPI MsiSourceListAddSourceW( LPCWSTR szProduct, LPCWSTR szUserName,
DWORD dwReserved, LPCWSTR szSource)
{
INT ret;
LPWSTR sidstr = NULL;
DWORD sidsize = 0;
DWORD domsize = 0;
TRACE("%s %s %s\n", debugstr_w(szProduct), debugstr_w(szUserName), debugstr_w(szSource));
if (LookupAccountNameW(NULL, szUserName, NULL, &sidsize, NULL, &domsize, NULL))
{
PSID psid = msi_alloc(sidsize);
if (LookupAccountNameW(NULL, szUserName, psid, &sidsize, NULL, &domsize, NULL))
ConvertSidToStringSidW(psid, &sidstr);
msi_free(psid);
}
ret = MsiSourceListAddSourceExW(szProduct, sidstr,
MSIINSTALLCONTEXT_USERMANAGED, MSISOURCETYPE_NETWORK, szSource, 0);
if (sidstr)
LocalFree(sidstr);
return ret;
}
/******************************************************************
* MsiSourceListAddSourceA (MSI.@)
*/
UINT WINAPI MsiSourceListAddSourceA( LPCSTR szProduct, LPCSTR szUserName,
DWORD dwReserved, LPCSTR szSource)
{
INT ret;
LPWSTR szwproduct;
LPWSTR szwusername;
LPWSTR szwsource;
szwproduct = strdupAtoW( szProduct );
szwusername = strdupAtoW( szUserName );
szwsource = strdupAtoW( szSource );
ret = MsiSourceListAddSourceW(szwproduct, szwusername, 0, szwsource);
msi_free(szwproduct);
msi_free(szwusername);
msi_free(szwsource);
return ret;
}
/******************************************************************
* MsiSourceListAddSourceExW (MSI.@)
*/
UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource,
DWORD dwIndex)
{
HKEY sourcekey;
HKEY typekey;
UINT rc;
media_info source_struct;
TRACE("%s %s %x %x %s %i\n", debugstr_w(szProduct), debugstr_w(szUserSid),
dwContext, dwOptions, debugstr_w(szSource), dwIndex);
if (!szProduct)
return ERROR_INVALID_PARAMETER;
if (!szSource)
return ERROR_INVALID_PARAMETER;
if (dwOptions & MSICODE_PATCH)
{
FIXME("Unhandled options MSICODE_PATCH\n");
return ERROR_FUNCTION_FAILED;
}
if (szUserSid)
FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
if (dwContext == MSIINSTALLCONTEXT_MACHINE)
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, FALSE, TRUE);
else
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, TRUE, TRUE);
if (rc != ERROR_SUCCESS)
return ERROR_UNKNOWN_PRODUCT;
if (dwOptions & MSISOURCETYPE_NETWORK)
rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
else if (dwOptions & MSISOURCETYPE_URL)
rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
else if (dwOptions & MSISOURCETYPE_MEDIA)
rc = OpenMediaSubkey(sourcekey, &typekey, TRUE);
else
{
ERR("unknown media type: %08x\n", dwOptions);
RegCloseKey(sourcekey);
return ERROR_FUNCTION_FAILED;
}
source_struct.szIndex[0] = 0;
if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS)
{
DWORD current_index = atoiW(source_struct.szIndex);
/* found the source */
if (dwIndex > 0 && current_index != dwIndex)
FIXME("Need to reorder the sources!\n");
msi_free( source_struct.path );
}
else
{
DWORD current_index = 0;
static const WCHAR fmt[] = {'%','i',0};
DWORD size = lstrlenW(szSource)*sizeof(WCHAR);
if (source_struct.szIndex[0])
current_index = atoiW(source_struct.szIndex);
/* new source */
if (dwIndex > 0 && dwIndex < current_index)
FIXME("Need to reorder the sources!\n");
current_index ++;
sprintfW(source_struct.szIndex,fmt,current_index);
rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ,
(const BYTE *)szSource, size);
}
RegCloseKey(typekey);
RegCloseKey(sourcekey);
return rc;
}
/******************************************************************
* MsiSourceListAddMediaDisk(MSI.@)
*/
UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid,
MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId,
LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
{
HKEY sourcekey;
HKEY mediakey;
UINT rc;
WCHAR szIndex[10];
static const WCHAR fmt[] = {'%','i',0};
static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
static const WCHAR empty[1] = {0};
LPCWSTR pt1,pt2;
LPWSTR buffer;
DWORD size;
TRACE("%s %s %x %x %i %s %s\n", debugstr_w(szProduct),
debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId,
debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt));
if (!szProduct || lstrlenW(szProduct) > 39)
return ERROR_INVALID_PARAMETER;
if (dwOptions & MSICODE_PATCH)
{
FIXME("Unhandled options MSICODE_PATCH\n");
return ERROR_FUNCTION_FAILED;
}
if (szUserSid)
FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
if (dwContext == MSIINSTALLCONTEXT_MACHINE)
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, FALSE, TRUE);
else
rc = OpenSourceKey(szProduct, &sourcekey, MSICODE_PRODUCT, TRUE, TRUE);
if (rc != ERROR_SUCCESS)
return ERROR_UNKNOWN_PRODUCT;
OpenMediaSubkey(sourcekey,&mediakey,TRUE);
sprintfW(szIndex,fmt,dwDiskId);
size = 2;
if (szVolumeLabel)
{
size +=lstrlenW(szVolumeLabel);
pt1 = szVolumeLabel;
}
else
pt1 = empty;
if (szDiskPrompt)
{
size +=lstrlenW(szDiskPrompt);
pt2 = szDiskPrompt;
}
else
pt2 = empty;
size *=sizeof(WCHAR);
buffer = msi_alloc(size);
sprintfW(buffer,disk_fmt,pt1,pt2);
RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
msi_free( buffer );
RegCloseKey(sourcekey);
RegCloseKey(mediakey);
return ERROR_SUCCESS;
}
/******************************************************************
* MsiSourceListAddSourceExA (MSI.@)
*/
UINT WINAPI MsiSourceListClearAllA( LPCSTR szProduct, LPCSTR szUserName, DWORD dwReserved )
{
FIXME("(%s %s %d)\n", debugstr_a(szProduct), debugstr_a(szUserName), dwReserved);
return ERROR_SUCCESS;
}
/******************************************************************
* MsiSourceListAddSourceExW (MSI.@)
*/
UINT WINAPI MsiSourceListClearAllW( LPCWSTR szProduct, LPCWSTR szUserName, DWORD dwReserved )
{
FIXME("(%s %s %d)\n", debugstr_w(szProduct), debugstr_w(szUserName), dwReserved);
return ERROR_SUCCESS;
}
--- NEW FILE: sql.y ---
%{
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "query.h"
#include "wine-lgpl/list.h"
#include "wine/debug.h"
#define YYLEX_PARAM info
#define YYPARSE_PARAM info
static int sql_error(const char *str);
WINE_DEFAULT_DEBUG_CHANNEL(msi);
typedef struct tag_SQL_input
{
MSIDATABASE *db;
LPCWSTR command;
DWORD n, len;
MSIVIEW **view; /* view structure for the resulting query */
struct list *mem;
} SQL_input;
static LPWSTR SQL_getstring( void *info, const struct sql_str *str );
static INT SQL_getint( void *info );
static int sql_lex( void *SQL_lval, SQL_input *info );
static void *parser_alloc( void *info, unsigned int sz );
static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column );
static BOOL SQL_MarkPrimaryKeys( column_info *cols, column_info *keys);
static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r );
static struct expr * EXPR_unary( void *info, struct expr *l, UINT op );
static struct expr * EXPR_column( void *info, const column_info *column );
static struct expr * EXPR_ival( void *info, int val );
static struct expr * EXPR_sval( void *info, const struct sql_str *str );
static struct expr * EXPR_wildcard( void *info );
%}
%pure-parser
%union
{
struct sql_str str;
LPWSTR string;
column_info *column_list;
MSIVIEW *query;
struct expr *expr;
USHORT column_type;
int integer;
}
%token TK_ALTER TK_AND TK_BY TK_CHAR TK_COMMA TK_CREATE TK_DELETE
%token TK_DISTINCT TK_DOT TK_EQ TK_FREE TK_FROM TK_GE TK_GT TK_HOLD TK_ADD
%token <str> TK_ID
%token TK_ILLEGAL TK_INSERT TK_INT
%token <str> TK_INTEGER
%token TK_INTO TK_IS TK_KEY TK_LE TK_LONG TK_LONGCHAR TK_LP TK_LT
%token TK_LOCALIZABLE TK_MINUS TK_NE TK_NOT TK_NULL
%token TK_OBJECT TK_OR TK_ORDER TK_PRIMARY TK_RP
%token TK_SELECT TK_SET TK_SHORT TK_SPACE TK_STAR
%token <str> TK_STRING
%token TK_TABLE TK_TEMPORARY TK_UPDATE TK_VALUES TK_WHERE TK_WILDCARD
/*
* These are extra tokens used by the lexer but never seen by the
* parser. We put them in a rule so that the parser generator will
* add them to the parse.h output file.
*
*/
%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
COLUMN AGG_FUNCTION.
%type <string> table id
%type <column_list> selcollist column column_and_type column_def table_def
%type <column_list> column_assignment update_assign_list constlist
%type <query> query from fromtable selectfrom unorderedsel
%type <query> oneupdate onedelete oneselect onequery onecreate oneinsert onealter
%type <expr> expr val column_val const_val
%type <column_type> column_type data_type data_type_l data_count
%type <integer> number alterop
/* Reference: http://mates.ms.mff.cuni.cz/oracle/doc/ora815nt/server.815/a67779/operator.htm */
%left TK_OR
%left TK_AND
%left TK_NOT
%left TK_EQ TK_NE TK_LT TK_GT TK_LE TK_GE TK_LIKE
%right TK_NEGATION
%%
query:
onequery
{
SQL_input* sql = (SQL_input*) info;
*sql->view = $1;
}
;
onequery:
oneselect
| onecreate
| oneinsert
| oneupdate
| onedelete
| onealter
;
oneinsert:
TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP
{
SQL_input *sql = (SQL_input*) info;
MSIVIEW *insert = NULL;
UINT r;
r = INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE );
if( !insert )
YYABORT;
$$ = insert;
}
| TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY
{
SQL_input *sql = (SQL_input*) info;
MSIVIEW *insert = NULL;
INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE );
if( !insert )
YYABORT;
$$ = insert;
}
;
onecreate:
TK_CREATE TK_TABLE table TK_LP table_def TK_RP
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *create = NULL;
if( !$5 )
YYABORT;
CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
if( !create )
YYABORT;
$$ = create;
}
| TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *create = NULL;
if( !$5 )
YYABORT;
CREATE_CreateView( sql->db, &create, $3, $5, TRUE );
if( !create )
YYABORT;
$$ = create;
}
;
oneupdate:
TK_UPDATE table TK_SET update_assign_list TK_WHERE expr
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *update = NULL;
UPDATE_CreateView( sql->db, &update, $2, $4, $6 );
if( !update )
YYABORT;
$$ = update;
}
| TK_UPDATE table TK_SET update_assign_list
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *update = NULL;
UPDATE_CreateView( sql->db, &update, $2, $4, NULL );
if( !update )
YYABORT;
$$ = update;
}
;
onedelete:
TK_DELETE from
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *delete = NULL;
DELETE_CreateView( sql->db, &delete, $2 );
if( !delete )
YYABORT;
$$ = delete;
}
;
onealter:
TK_ALTER TK_TABLE table alterop
{
SQL_input* sql = (SQL_input*) info;
MSIVIEW *alter = NULL;
ALTER_CreateView( sql->db, &alter, $3, NULL, $4 );
if( !alter )
YYABORT;
$$ = alter;
}
| TK_ALTER TK_TABLE table TK_ADD column_and_type
{
SQL_input *sql = (SQL_input *)info;
MSIVIEW *alter = NULL;
ALTER_CreateView( sql->db, &alter, $3, $5, 0 );
if (!alter)
YYABORT;
$$ = alter;
}
| TK_ALTER TK_TABLE table TK_ADD column_and_type TK_HOLD
{
SQL_input *sql = (SQL_input *)info;
MSIVIEW *alter = NULL;
ALTER_CreateView( sql->db, &alter, $3, $5, 1 );
if (!alter)
YYABORT;
$$ = alter;
}
;
alterop:
TK_HOLD
{
$$ = 1;
}
| TK_FREE
{
$$ = -1;
}
;
table_def:
column_def TK_PRIMARY TK_KEY selcollist
{
if( SQL_MarkPrimaryKeys( $1, $4 ) )
$$ = $1;
else
$$ = NULL;
}
;
column_def:
column_def TK_COMMA column_and_type
{
column_info *ci;
for( ci = $1; ci->next; ci = ci->next )
;
ci->next = $3;
$$ = $1;
}
| column_and_type
{
$$ = $1;
}
;
column_and_type:
column column_type
{
$$ = $1;
$$->type = ($2 | MSITYPE_VALID);
$$->temporary = $2 & MSITYPE_TEMPORARY ? TRUE : FALSE;
}
;
column_type:
data_type_l
{
$$ = $1;
}
| data_type_l TK_LOCALIZABLE
{
$$ = $1 | MSITYPE_LOCALIZABLE;
}
| data_type_l TK_TEMPORARY
{
$$ = $1 | MSITYPE_TEMPORARY;
}
;
data_type_l:
data_type
{
$$ |= MSITYPE_NULLABLE;
}
| data_type TK_NOT TK_NULL
{
$$ = $1;
}
;
data_type:
TK_CHAR
{
$$ = MSITYPE_STRING | 1;
}
| TK_CHAR TK_LP data_count TK_RP
{
$$ = MSITYPE_STRING | 0x400 | $3;
}
| TK_LONGCHAR
{
$$ = 2;
}
| TK_SHORT
{
$$ = 2;
}
| TK_INT
{
$$ = 2;
}
| TK_LONG
{
$$ = 4;
}
| TK_OBJECT
{
$$ = MSITYPE_STRING | MSITYPE_VALID;
}
;
data_count:
number
{
if( ( $1 > 255 ) || ( $1 < 0 ) )
YYABORT;
$$ = $1;
}
;
oneselect:
unorderedsel TK_ORDER TK_BY selcollist
{
SQL_input* sql = (SQL_input*) info;
$$ = NULL;
if( $4 )
ORDER_CreateView( sql->db, &$$, $1, $4 );
else
$$ = $1;
if( !$$ )
YYABORT;
}
| unorderedsel
;
unorderedsel:
TK_SELECT selectfrom
{
$$ = $2;
}
| TK_SELECT TK_DISTINCT selectfrom
{
SQL_input* sql = (SQL_input*) info;
UINT r;
$$ = NULL;
r = DISTINCT_CreateView( sql->db, &$$, $3 );
if (r != ERROR_SUCCESS)
{
$3->ops->delete($3);
YYABORT;
}
}
;
selectfrom:
selcollist from
{
SQL_input* sql = (SQL_input*) info;
UINT r;
$$ = NULL;
if( $1 )
{
r = SELECT_CreateView( sql->db, &$$, $2, $1 );
if (r != ERROR_SUCCESS)
{
$2->ops->delete($2);
YYABORT;
}
}
else
$$ = $2;
}
;
selcollist:
column
| column TK_COMMA selcollist
{
$1->next = $3;
}
| TK_STAR
{
$$ = NULL;
}
;
from:
fromtable
| fromtable TK_WHERE expr
{
SQL_input* sql = (SQL_input*) info;
UINT r;
$$ = NULL;
r = WHERE_CreateView( sql->db, &$$, $1, $3 );
if( r != ERROR_SUCCESS )
{
$1->ops->delete( $1 );
YYABORT;
}
}
;
fromtable:
TK_FROM table
{
SQL_input* sql = (SQL_input*) info;
UINT r;
$$ = NULL;
r = TABLE_CreateView( sql->db, $2, &$$ );
if( r != ERROR_SUCCESS || !$$ )
YYABORT;
}
| TK_FROM table TK_COMMA table
{
SQL_input* sql = (SQL_input*) info;
UINT r;
/* only support inner joins on two tables */
r = JOIN_CreateView( sql->db, &$$, $2, $4 );
if( r != ERROR_SUCCESS )
YYABORT;
}
;
expr:
TK_LP expr TK_RP
{
$$ = $2;
if( !$$ )
YYABORT;
}
| expr TK_AND expr
{
$$ = EXPR_complex( info, $1, OP_AND, $3 );
if( !$$ )
YYABORT;
}
| expr TK_OR expr
{
$$ = EXPR_complex( info, $1, OP_OR, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_EQ val
{
$$ = EXPR_complex( info, $1, OP_EQ, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_GT val
{
$$ = EXPR_complex( info, $1, OP_GT, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_LT val
{
$$ = EXPR_complex( info, $1, OP_LT, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_LE val
{
$$ = EXPR_complex( info, $1, OP_LE, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_GE val
{
$$ = EXPR_complex( info, $1, OP_GE, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_NE val
{
$$ = EXPR_complex( info, $1, OP_NE, $3 );
if( !$$ )
YYABORT;
}
| column_val TK_IS TK_NULL
{
$$ = EXPR_unary( info, $1, OP_ISNULL );
if( !$$ )
YYABORT;
}
| column_val TK_IS TK_NOT TK_NULL
{
$$ = EXPR_unary( info, $1, OP_NOTNULL );
if( !$$ )
YYABORT;
}
;
val:
column_val
| const_val
;
constlist:
const_val
{
$$ = parser_alloc_column( info, NULL, NULL );
if( !$$ )
YYABORT;
$$->val = $1;
}
| const_val TK_COMMA constlist
{
$$ = parser_alloc_column( info, NULL, NULL );
if( !$$ )
YYABORT;
$$->val = $1;
$$->next = $3;
}
;
update_assign_list:
column_assignment
| column_assignment TK_COMMA update_assign_list
{
$$ = $1;
$$->next = $3;
}
;
column_assignment:
column TK_EQ const_val
{
$$ = $1;
$$->val = $3;
}
;
const_val:
number
{
$$ = EXPR_ival( info, $1 );
if( !$$ )
YYABORT;
}
| TK_MINUS number %prec TK_NEGATION
{
$$ = EXPR_ival( info, -$2 );
if( !$$ )
YYABORT;
}
| TK_STRING
{
$$ = EXPR_sval( info, &$1 );
if( !$$ )
YYABORT;
}
| TK_WILDCARD
{
$$ = EXPR_wildcard( info );
if( !$$ )
YYABORT;
}
;
column_val:
column
{
$$ = EXPR_column( info, $1 );
if( !$$ )
YYABORT;
}
;
column:
table TK_DOT id
{
$$ = parser_alloc_column( info, $1, $3 );
if( !$$ )
YYABORT;
}
| id
{
$$ = parser_alloc_column( info, NULL, $1 );
if( !$$ )
YYABORT;
}
;
table:
id
{
$$ = $1;
}
;
id:
TK_ID
{
$$ = SQL_getstring( info, &$1 );
if( !$$ )
YYABORT;
}
;
number:
TK_INTEGER
{
$$ = SQL_getint( info );
}
;
%%
static void *parser_alloc( void *info, unsigned int sz )
{
SQL_input* sql = (SQL_input*) info;
struct list *mem;
mem = msi_alloc( sizeof (struct list) + sz );
list_add_tail( sql->mem, mem );
return &mem[1];
}
static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column )
{
column_info *col;
col = parser_alloc( info, sizeof (*col) );
if( col )
{
col->table = table;
col->column = column;
col->val = NULL;
col->type = 0;
col->next = NULL;
}
return col;
}
static int sql_lex( void *SQL_lval, SQL_input *sql )
{
int token;
struct sql_str * str = SQL_lval;
do
{
sql->n += sql->len;
if( ! sql->command[sql->n] )
return 0; /* end of input */
/* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */
sql->len = sqliteGetToken( &sql->command[sql->n], &token );
if( sql->len==0 )
break;
str->data = &sql->command[sql->n];
str->len = sql->len;
}
while( token == TK_SPACE );
/* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */
return token;
}
LPWSTR SQL_getstring( void *info, const struct sql_str *strdata )
{
LPCWSTR p = strdata->data;
UINT len = strdata->len;
LPWSTR str;
/* if there's quotes, remove them */
if( ( (p[0]=='`') && (p[len-1]=='`') ) ||
( (p[0]=='\'') && (p[len-1]=='\'') ) )
{
p++;
len -= 2;
}
str = parser_alloc( info, (len + 1)*sizeof(WCHAR) );
if( !str )
return str;
memcpy( str, p, len*sizeof(WCHAR) );
str[len]=0;
return str;
}
INT SQL_getint( void *info )
{
SQL_input* sql = (SQL_input*) info;
LPCWSTR p = &sql->command[sql->n];
INT i, r = 0;
for( i=0; i<sql->len; i++ )
{
if( '0' > p[i] || '9' < p[i] )
{
ERR("should only be numbers here!\n");
break;
}
r = (p[i]-'0') + r*10;
}
return r;
}
static int sql_error( const char *str )
{
return 0;
}
static struct expr * EXPR_wildcard( void *info )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_WILDCARD;
}
return e;
}
static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_COMPLEX;
e->u.expr.left = l;
e->u.expr.op = op;
e->u.expr.right = r;
}
return e;
}
static struct expr * EXPR_unary( void *info, struct expr *l, UINT op )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_UNARY;
e->u.expr.left = l;
e->u.expr.op = op;
e->u.expr.right = NULL;
}
return e;
}
static struct expr * EXPR_column( void *info, const column_info *column )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_COLUMN;
e->u.sval = column->column;
}
return e;
}
static struct expr * EXPR_ival( void *info, int val )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_IVAL;
e->u.ival = val;
}
return e;
}
static struct expr * EXPR_sval( void *info, const struct sql_str *str )
{
struct expr *e = parser_alloc( info, sizeof *e );
if( e )
{
e->type = EXPR_SVAL;
e->u.sval = SQL_getstring( info, str );
}
return e;
}
static BOOL SQL_MarkPrimaryKeys( column_info *cols,
column_info *keys )
{
column_info *k;
BOOL found = TRUE;
for( k = keys; k && found; k = k->next )
{
column_info *c;
found = FALSE;
for( c = cols; c && !found; c = c->next )
{
if( lstrcmpW( k->column, c->column ) )
continue;
c->type |= MSITYPE_KEY;
found = TRUE;
}
}
return found;
}
UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview,
struct list *mem )
{
SQL_input sql;
int r;
*phview = NULL;
sql.db = db;
sql.command = command;
sql.n = 0;
sql.len = 0;
sql.view = phview;
sql.mem = mem;
r = sql_parse(&sql);
TRACE("Parse returned %d\n", r);
if( r )
{
*sql.view = NULL;
return ERROR_BAD_QUERY_SYNTAX;
}
return ERROR_SUCCESS;
}
--- NEW FILE: streams.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2007 James Hawkins
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "msipriv.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
#define NUM_STREAMS_COLS 2
#define MAX_STREAM_NAME_LEN 62
typedef struct tabSTREAM
{
int str_index;
LPWSTR name;
IStream *stream;
} STREAM;
typedef struct tagMSISTREAMSVIEW
{
MSIVIEW view;
MSIDATABASE *db;
STREAM **streams;
UINT max_streams;
UINT num_rows;
UINT row_size;
} MSISTREAMSVIEW;
static BOOL add_stream_to_table(MSISTREAMSVIEW *sv, STREAM *stream, int index)
{
if (index >= sv->max_streams)
{
sv->max_streams *= 2;
sv->streams = msi_realloc(sv->streams, sv->max_streams * sizeof(STREAM *));
if (!sv->streams)
return FALSE;
}
sv->streams[index] = stream;
return TRUE;
}
static STREAM *create_stream(MSISTREAMSVIEW *sv, LPWSTR name, BOOL encoded, IStream *stm)
{
STREAM *stream;
WCHAR decoded[MAX_STREAM_NAME_LEN];
LPWSTR ptr = name;
stream = msi_alloc(sizeof(STREAM));
if (!stream)
return NULL;
if (encoded)
{
decode_streamname(name, decoded);
ptr = decoded;
TRACE("stream -> %s %s\n", debugstr_w(name), debugstr_w(decoded));
}
stream->name = strdupW(ptr);
if (!stream->name)
{
msi_free(stream);
return NULL;
}
stream->str_index = msi_addstringW(sv->db->strings, 0, stream->name, -1, 1, StringNonPersistent);
stream->stream = stm;
return stream;
}
static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
if (col != 1)
return ERROR_INVALID_PARAMETER;
if (row >= sv->num_rows)
return ERROR_NO_MORE_ITEMS;
*val = sv->streams[row]->str_index;
return ERROR_SUCCESS;
}
static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
if (row >= sv->num_rows)
return ERROR_FUNCTION_FAILED;
IStream_AddRef(sv->streams[row]->stream);
*stm = sv->streams[row]->stream;
return ERROR_SUCCESS;
}
static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
{
FIXME("(%p, %d, %p, %d): stub!\n", view, row, rec, mask);
return ERROR_SUCCESS;
}
static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temporary)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
STREAM *stream;
IStream *stm;
STATSTG stat;
LPWSTR name = NULL;
USHORT *data = NULL;
HRESULT hr;
ULONG count;
UINT r = ERROR_FUNCTION_FAILED;
TRACE("(%p, %p, %d)\n", view, rec, temporary);
r = MSI_RecordGetIStream(rec, 2, &stm);
if (r != ERROR_SUCCESS)
return r;
hr = IStream_Stat(stm, &stat, STATFLAG_NONAME);
if (FAILED(hr))
{
WARN("failed to stat stream: %08x\n", hr);
goto done;
}
if (stat.cbSize.QuadPart >> 32)
goto done;
data = msi_alloc(stat.cbSize.QuadPart);
if (!data)
goto done;
hr = IStream_Read(stm, data, stat.cbSize.QuadPart, &count);
if (FAILED(hr) || count != stat.cbSize.QuadPart)
{
WARN("failed to read stream: %08x\n", hr);
goto done;
}
name = strdupW(MSI_RecordGetString(rec, 1));
if (!name)
goto done;
r = write_stream_data(sv->db->storage, name, data, count, FALSE);
if (r != ERROR_SUCCESS)
{
WARN("failed to write stream data: %d\n", r);
goto done;
}
stream = create_stream(sv, name, FALSE, NULL);
if (!stream)
goto done;
IStorage_OpenStream(sv->db->storage, name, 0,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream);
if (!add_stream_to_table(sv, stream, sv->num_rows++))
goto done;
done:
msi_free(name);
msi_free(data);
IStream_Release(stm);
return r;
}
static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
{
FIXME("(%p %d): stub!\n", view, row);
return ERROR_SUCCESS;
}
static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
{
TRACE("(%p, %p)\n", view, record);
return ERROR_SUCCESS;
}
static UINT STREAMS_close(struct tagMSIVIEW *view)
{
TRACE("(%p)\n", view);
return ERROR_SUCCESS;
}
static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
TRACE("(%p, %p, %p)\n", view, rows, cols);
if (cols) *cols = NUM_STREAMS_COLS;
if (rows) *rows = sv->num_rows;
return ERROR_SUCCESS;
}
static UINT STREAMS_get_column_info(struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type)
{
LPCWSTR name_ptr = NULL;
static const WCHAR Name[] = {'N','a','m','e',0};
static const WCHAR Data[] = {'D','a','t','a',0};
TRACE("(%p, %d, %p, %p)\n", view, n, name, type);
if (n == 0 || n > NUM_STREAMS_COLS)
return ERROR_INVALID_PARAMETER;
switch (n)
{
case 1:
name_ptr = Name;
if (type) *type = MSITYPE_STRING | MAX_STREAM_NAME_LEN;
break;
case 2:
name_ptr = Data;
if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
break;
}
if (name)
{
*name = strdupW(name_ptr);
if (!*name) return ERROR_FUNCTION_FAILED;
}
return ERROR_SUCCESS;
}
static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec)
{
FIXME("(%p, %d, %p): stub!\n", view, eModifyMode, rec);
return ERROR_SUCCESS;
}
static UINT STREAMS_delete(struct tagMSIVIEW *view)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
int i;
TRACE("(%p)\n", view);
for (i = 0; i < sv->num_rows; i++)
{
if (sv->streams[i]->stream)
IStream_Release(sv->streams[i]->stream);
msi_free(sv->streams[i]);
}
msi_free(sv->streams);
return ERROR_SUCCESS;
}
static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle)
{
MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
UINT index = (UINT)*handle;
TRACE("(%d, %d): %d\n", *row, col, val);
if (col == 0 || col > NUM_STREAMS_COLS)
return ERROR_INVALID_PARAMETER;
while (index < sv->num_rows)
{
if (sv->streams[index]->str_index == val)
{
*row = index;
break;
}
index++;
}
*handle = (MSIITERHANDLE)++index;
if (index >= sv->num_rows)
return ERROR_NO_MORE_ITEMS;
return ERROR_SUCCESS;
}
static const MSIVIEWOPS streams_ops =
{
STREAMS_fetch_int,
STREAMS_fetch_stream,
STREAMS_set_row,
STREAMS_insert_row,
STREAMS_delete_row,
STREAMS_execute,
STREAMS_close,
STREAMS_get_dimensions,
STREAMS_get_column_info,
STREAMS_modify,
STREAMS_delete,
STREAMS_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
static UINT add_streams_to_table(MSISTREAMSVIEW *sv)
{
IEnumSTATSTG *stgenum = NULL;
STATSTG stat;
STREAM *stream = NULL;
HRESULT hr;
UINT count = 0, size;
hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum);
if (FAILED(hr))
return -1;
sv->max_streams = 1;
sv->streams = msi_alloc(sizeof(STREAM *));
if (!sv->streams)
return -1;
while (TRUE)
{
size = 0;
hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size);
if (FAILED(hr) || !size)
break;
/* table streams are not in the _Streams table */
if (*stat.pwcsName == 0x4840)
continue;
stream = create_stream(sv, stat.pwcsName, TRUE, NULL);
if (!stream)
{
count = -1;
break;
}
IStorage_OpenStream(sv->db->storage, stat.pwcsName, 0,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream);
if (!add_stream_to_table(sv, stream, count++))
{
count = -1;
break;
}
}
IEnumSTATSTG_Release(stgenum);
return count;
}
UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
{
MSISTREAMSVIEW *sv;
TRACE("(%p, %p)\n", db, view);
sv = msi_alloc(sizeof(MSISTREAMSVIEW));
if (!sv)
return ERROR_FUNCTION_FAILED;
sv->view.ops = &streams_ops;
sv->db = db;
sv->num_rows = add_streams_to_table(sv);
if (sv->num_rows < 0)
return ERROR_FUNCTION_FAILED;
*view = (MSIVIEW *)sv;
return ERROR_SUCCESS;
}
--- NEW FILE: string.c ---
/*
* String Table Functions
*
* Copyright 2002-2004, Mike McCormack for CodeWeavers
* Copyright 2007 Robert Shearman for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include <stdarg.h>
#include <assert.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
#define HASH_SIZE 0x101
#define LONG_STR_BYTES 3
typedef struct _msistring
{
int hash_next;
UINT persistent_refcount;
UINT nonpersistent_refcount;
LPWSTR str;
} msistring;
struct string_table
{
UINT maxcount; /* the number of strings */
UINT freeslot;
UINT codepage;
int hash[HASH_SIZE];
msistring *strings; /* an array of strings (in the tree) */
};
static UINT msistring_makehash( const WCHAR *str )
{
UINT hash = 0;
if (str==NULL)
return hash;
while( *str )
{
hash ^= *str++;
hash *= 53;
hash = (hash<<5) | (hash>>27);
}
return hash % HASH_SIZE;
}
static string_table *init_stringtable( int entries, UINT codepage )
{
string_table *st;
int i;
if (codepage != CP_ACP && !IsValidCodePage(codepage))
{
ERR("invalid codepage %d\n", codepage);
return NULL;
}
st = msi_alloc( sizeof (string_table) );
if( !st )
return NULL;
if( entries < 1 )
entries = 1;
st->strings = msi_alloc_zero( sizeof (msistring) * entries );
if( !st->strings )
{
msi_free( st );
return NULL;
}
st->maxcount = entries;
st->freeslot = 1;
st->codepage = codepage;
for( i=0; i<HASH_SIZE; i++ )
st->hash[i] = -1;
return st;
}
VOID msi_destroy_stringtable( string_table *st )
{
UINT i;
for( i=0; i<st->maxcount; i++ )
{
if( st->strings[i].persistent_refcount ||
st->strings[i].nonpersistent_refcount )
msi_free( st->strings[i].str );
}
msi_free( st->strings );
msi_free( st );
}
static int st_find_free_entry( string_table *st )
{
UINT i, sz;
msistring *p;
TRACE("%p\n", st);
if( st->freeslot )
{
for( i = st->freeslot; i < st->maxcount; i++ )
if( !st->strings[i].persistent_refcount &&
!st->strings[i].nonpersistent_refcount )
return i;
}
for( i = 1; i < st->maxcount; i++ )
if( !st->strings[i].persistent_refcount &&
!st->strings[i].nonpersistent_refcount )
return i;
/* dynamically resize */
sz = st->maxcount + 1 + st->maxcount/2;
p = msi_realloc_zero( st->strings, sz*sizeof(msistring) );
if( !p )
return -1;
st->strings = p;
st->freeslot = st->maxcount;
st->maxcount = sz;
if( st->strings[st->freeslot].persistent_refcount ||
st->strings[st->freeslot].nonpersistent_refcount )
ERR("oops. expected freeslot to be free...\n");
return st->freeslot;
}
static void set_st_entry( string_table *st, UINT n, LPWSTR str, UINT refcount, enum StringPersistence persistence )
{
UINT hash = msistring_makehash( str );
if (persistence == StringPersistent)
{
st->strings[n].persistent_refcount = refcount;
st->strings[n].nonpersistent_refcount = 0;
}
else
{
st->strings[n].persistent_refcount = 0;
st->strings[n].nonpersistent_refcount = refcount;
}
st->strings[n].str = str;
st->strings[n].hash_next = st->hash[hash];
st->hash[hash] = n;
if( n < st->maxcount )
st->freeslot = n + 1;
}
static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, UINT refcount, enum StringPersistence persistence )
{
LPWSTR str;
int sz;
if( !data )
return 0;
if( !data[0] )
return 0;
if( n > 0 )
{
if( st->strings[n].persistent_refcount ||
st->strings[n].nonpersistent_refcount )
return -1;
}
else
{
if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
{
if (persistence == StringPersistent)
st->strings[n].persistent_refcount += refcount;
else
st->strings[n].nonpersistent_refcount += refcount;
return n;
}
n = st_find_free_entry( st );
if( n < 0 )
return -1;
}
if( n < 1 )
{
ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n );
return -1;
}
/* allocate a new string */
if( len < 0 )
len = strlen(data);
sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 );
str = msi_alloc( (sz+1)*sizeof(WCHAR) );
if( !str )
return -1;
MultiByteToWideChar( st->codepage, 0, data, len, str, sz );
str[sz] = 0;
set_st_entry( st, n, str, refcount, persistence );
return n;
}
int msi_addstringW( string_table *st, UINT n, const WCHAR *data, int len, UINT refcount, enum StringPersistence persistence )
{
LPWSTR str;
/* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
if( !data )
return 0;
if( !data[0] )
return 0;
if( n > 0 )
{
if( st->strings[n].persistent_refcount ||
st->strings[n].nonpersistent_refcount )
return -1;
}
else
{
if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
{
if (persistence == StringPersistent)
st->strings[n].persistent_refcount += refcount;
else
st->strings[n].nonpersistent_refcount += refcount;
return n;
}
n = st_find_free_entry( st );
if( n < 0 )
return -1;
}
if( n < 1 )
{
ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
return -1;
}
/* allocate a new string */
if(len<0)
len = strlenW(data);
TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len );
str = msi_alloc( (len+1)*sizeof(WCHAR) );
if( !str )
return -1;
memcpy( str, data, len*sizeof(WCHAR) );
str[len] = 0;
set_st_entry( st, n, str, refcount, persistence );
return n;
}
/* find the string identified by an id - return null if there's none */
const WCHAR *msi_string_lookup_id( const string_table *st, UINT id )
{
static const WCHAR zero[] = { 0 };
if( id == 0 )
return zero;
if( id >= st->maxcount )
return NULL;
if( id && !st->strings[id].persistent_refcount && !st->strings[id].nonpersistent_refcount)
return NULL;
return st->strings[id].str;
}
/*
* msi_id2stringW
*
* [in] st - pointer to the string table
* [in] id - id of the string to retrieve
* [out] buffer - destination of the string
* [in/out] sz - number of bytes available in the buffer on input
* number of bytes used on output
*
* The size includes the terminating nul character. Short buffers
* will be filled, but not nul terminated.
*/
UINT msi_id2stringW( const string_table *st, UINT id, LPWSTR buffer, UINT *sz )
{
UINT len;
const WCHAR *str;
TRACE("Finding string %d of %d\n", id, st->maxcount);
str = msi_string_lookup_id( st, id );
if( !str )
return ERROR_FUNCTION_FAILED;
len = strlenW( str ) + 1;
if( !buffer )
{
*sz = len;
return ERROR_SUCCESS;
}
if( *sz < len )
*sz = len;
memcpy( buffer, str, (*sz)*sizeof(WCHAR) );
*sz = len;
return ERROR_SUCCESS;
}
/*
* msi_id2stringA
*
* [in] st - pointer to the string table
* [in] id - id of the string to retrieve
* [out] buffer - destination of the UTF8 string
* [in/out] sz - number of bytes available in the buffer on input
* number of bytes used on output
*
* The size includes the terminating nul character. Short buffers
* will be filled, but not nul terminated.
*/
UINT msi_id2stringA( const string_table *st, UINT id, LPSTR buffer, UINT *sz )
{
UINT len;
const WCHAR *str;
int n;
TRACE("Finding string %d of %d\n", id, st->maxcount);
str = msi_string_lookup_id( st, id );
if( !str )
return ERROR_FUNCTION_FAILED;
len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL );
if( !buffer )
{
*sz = len;
return ERROR_SUCCESS;
}
if( len > *sz )
{
n = strlenW( str ) + 1;
while( n && (len > *sz) )
len = WideCharToMultiByte( st->codepage, 0,
str, --n, NULL, 0, NULL, NULL );
}
else
n = -1;
*sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL );
return ERROR_SUCCESS;
}
/*
* msi_string2idW
*
* [in] st - pointer to the string table
* [in] str - string to find in the string table
* [out] id - id of the string, if found
*/
UINT msi_string2idW( const string_table *st, LPCWSTR str, UINT *id )
{
UINT n, hash = msistring_makehash( str );
msistring *se = st->strings;
for (n = st->hash[hash]; n != -1; n = st->strings[n].hash_next )
{
if ((str == se[n].str) || !lstrcmpW(str, se[n].str))
{
*id = n;
return ERROR_SUCCESS;
}
}
return ERROR_INVALID_PARAMETER;
}
UINT msi_string2idA( const string_table *st, LPCSTR buffer, UINT *id )
{
DWORD sz;
UINT r = ERROR_INVALID_PARAMETER;
LPWSTR str;
TRACE("Finding string %s in string table\n", debugstr_a(buffer) );
if( buffer[0] == 0 )
{
*id = 0;
return ERROR_SUCCESS;
}
sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 );
if( sz <= 0 )
return r;
str = msi_alloc( sz*sizeof(WCHAR) );
if( !str )
return ERROR_NOT_ENOUGH_MEMORY;
MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz );
r = msi_string2idW( st, str, id );
msi_free( str );
return r;
}
UINT msi_strcmp( const string_table *st, UINT lval, UINT rval, UINT *res )
{
const WCHAR *l_str, *r_str;
l_str = msi_string_lookup_id( st, lval );
if( !l_str )
return ERROR_INVALID_PARAMETER;
r_str = msi_string_lookup_id( st, rval );
if( !r_str )
return ERROR_INVALID_PARAMETER;
/* does this do the right thing for all UTF-8 strings? */
*res = strcmpW( l_str, r_str );
return ERROR_SUCCESS;
}
static void string_totalsize( const string_table *st, UINT *datasize, UINT *poolsize )
{
UINT i, len, max, holesize;
if( st->strings[0].str || st->strings[0].persistent_refcount || st->strings[0].nonpersistent_refcount)
ERR("oops. element 0 has a string\n");
*poolsize = 4;
*datasize = 0;
max = 1;
holesize = 0;
for( i=1; i<st->maxcount; i++ )
{
if( !st->strings[i].persistent_refcount )
continue;
if( st->strings[i].str )
{
TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str));
len = WideCharToMultiByte( st->codepage, 0,
st->strings[i].str, -1, NULL, 0, NULL, NULL);
if( len )
len--;
(*datasize) += len;
if (len>0xffff)
(*poolsize) += 4;
max = i + 1;
(*poolsize) += holesize + 4;
holesize = 0;
}
else
holesize += 4;
}
TRACE("data %u pool %u codepage %x\n", *datasize, *poolsize, st->codepage );
}
static const WCHAR szStringData[] = {
'_','S','t','r','i','n','g','D','a','t','a',0 };
static const WCHAR szStringPool[] = {
'_','S','t','r','i','n','g','P','o','o','l',0 };
HRESULT msi_init_string_table( IStorage *stg )
{
USHORT zero[2] = { 0, 0 };
UINT ret;
/* create the StringPool stream... add the zero string to it*/
ret = write_stream_data(stg, szStringPool, zero, sizeof zero, TRUE);
if (ret != ERROR_SUCCESS)
return E_FAIL;
/* create the StringData stream... make it zero length */
ret = write_stream_data(stg, szStringData, NULL, 0, TRUE);
if (ret != ERROR_SUCCESS)
return E_FAIL;
return S_OK;
}
string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref )
{
string_table *st = NULL;
CHAR *data = NULL;
USHORT *pool = NULL;
UINT r, datasize = 0, poolsize = 0, codepage;
DWORD i, count, offset, len, n, refs;
r = read_stream_data( stg, szStringPool, TRUE, (BYTE **)&pool, &poolsize );
if( r != ERROR_SUCCESS)
goto end;
r = read_stream_data( stg, szStringData, TRUE, (BYTE **)&data, &datasize );
if( r != ERROR_SUCCESS)
goto end;
if ( (poolsize > 4) && (pool[1] & 0x8000) )
*bytes_per_strref = LONG_STR_BYTES;
else
*bytes_per_strref = sizeof(USHORT);
count = poolsize/4;
if( poolsize > 4 )
codepage = pool[0] | ( (pool[1] & ~0x8000) << 16 );
else
codepage = CP_ACP;
st = init_stringtable( count, codepage );
if (!st)
goto end;
offset = 0;
n = 1;
i = 1;
while( i<count )
{
/* the string reference count is always the second word */
refs = pool[i*2+1];
/* empty entries have two zeros, still have a string id */
if (pool[i*2] == 0 && refs == 0)
{
i++;
n++;
continue;
}
/*
* If a string is over 64k, the previous string entry is made null
* and its the high word of the length is inserted in the null string's
* reference count field.
*/
if( pool[i*2] == 0)
{
len = (pool[i*2+3] << 16) + pool[i*2+2];
i += 2;
}
else
{
len = pool[i*2];
i += 1;
}
if ( (offset + len) > datasize )
{
ERR("string table corrupt?\n");
break;
}
r = msi_addstring( st, n, data+offset, len, refs, StringPersistent );
if( r != n )
ERR("Failed to add string %d\n", n );
n++;
offset += len;
}
if ( datasize != offset )
ERR("string table load failed! (%08x != %08x), please report\n", datasize, offset );
TRACE("Loaded %d strings\n", count);
end:
msi_free( pool );
msi_free( data );
return st;
}
UINT msi_save_string_table( const string_table *st, IStorage *storage )
{
UINT i, datasize = 0, poolsize = 0, sz, used, r, codepage, n;
UINT ret = ERROR_FUNCTION_FAILED;
CHAR *data = NULL;
USHORT *pool = NULL;
TRACE("\n");
/* construct the new table in memory first */
string_totalsize( st, &datasize, &poolsize );
TRACE("%u %u %u\n", st->maxcount, datasize, poolsize );
pool = msi_alloc( poolsize );
if( ! pool )
{
WARN("Failed to alloc pool %d bytes\n", poolsize );
goto err;
}
data = msi_alloc( datasize );
if( ! data )
{
WARN("Failed to alloc data %d bytes\n", poolsize );
goto err;
}
used = 0;
codepage = st->codepage;
pool[0]=codepage&0xffff;
pool[1]=(codepage>>16);
n = 1;
for( i=1; i<st->maxcount; i++ )
{
if( !st->strings[i].persistent_refcount )
continue;
sz = datasize - used;
r = msi_id2stringA( st, i, data+used, &sz );
if( r != ERROR_SUCCESS )
{
ERR("failed to fetch string\n");
sz = 0;
}
if( sz && (sz < (datasize - used ) ) )
sz--;
if (sz)
pool[ n*2 + 1 ] = st->strings[i].persistent_refcount;
else
pool[ n*2 + 1 ] = 0;
if (sz < 0x10000)
{
pool[ n*2 ] = sz;
n++;
}
else
{
pool[ n*2 ] = 0;
pool[ n*2 + 2 ] = sz&0xffff;
pool[ n*2 + 3 ] = (sz>>16);
n += 2;
}
used += sz;
if( used > datasize )
{
ERR("oops overran %d >= %d\n", used, datasize);
goto err;
}
}
if( used != datasize )
{
ERR("oops used %d != datasize %d\n", used, datasize);
goto err;
}
/* write the streams */
r = write_stream_data( storage, szStringData, data, datasize, TRUE );
TRACE("Wrote StringData r=%08x\n", r);
if( r )
goto err;
r = write_stream_data( storage, szStringPool, pool, poolsize, TRUE );
TRACE("Wrote StringPool r=%08x\n", r);
if( r )
goto err;
ret = ERROR_SUCCESS;
err:
msi_free( data );
msi_free( pool );
return ret;
}
--- NEW FILE: suminfo.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002, 2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "shlwapi.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "wine-lgpl/msidefs.h"
#include "msipriv.h"
#include "objidl.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
#include "pshpack1.h"
typedef struct {
WORD wByteOrder;
WORD wFormat;
DWORD dwOSVer;
CLSID clsID;
DWORD reserved;
} PROPERTYSETHEADER;
typedef struct {
FMTID fmtid;
DWORD dwOffset;
} FORMATIDOFFSET;
typedef struct {
DWORD cbSection;
DWORD cProperties;
} PROPERTYSECTIONHEADER;
typedef struct {
DWORD propid;
DWORD dwOffset;
} PROPERTYIDOFFSET;
typedef struct {
DWORD type;
union {
INT i4;
SHORT i2;
FILETIME ft;
struct {
DWORD len;
BYTE str[1];
} str;
} u;
} PROPERTY_DATA;
#include "poppack.h"
#define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER))
static const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y',
'I','n','f','o','r','m','a','t','i','o','n',0 };
static void free_prop( PROPVARIANT *prop )
{
if (prop->vt == VT_LPSTR )
msi_free( prop->u.pszVal );
prop->vt = VT_EMPTY;
}
static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg )
{
MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg;
DWORD i;
for( i = 0; i < MSI_MAX_PROPS; i++ )
free_prop( &si->property[i] );
IStorage_Release( si->storage );
}
static UINT get_type( UINT uiProperty )
{
switch( uiProperty )
{
case PID_CODEPAGE:
return VT_I2;
case PID_SUBJECT:
case PID_AUTHOR:
case PID_KEYWORDS:
case PID_COMMENTS:
case PID_TEMPLATE:
case PID_LASTAUTHOR:
case PID_REVNUMBER:
case PID_APPNAME:
case PID_TITLE:
return VT_LPSTR;
case PID_LASTPRINTED:
case PID_CREATE_DTM:
case PID_LASTSAVE_DTM:
return VT_FILETIME;
case PID_WORDCOUNT:
case PID_CHARCOUNT:
case PID_SECURITY:
case PID_PAGECOUNT:
return VT_I4;
}
return VT_EMPTY;
}
static UINT get_property_count( const PROPVARIANT *property )
{
UINT i, n = 0;
if( !property )
return n;
for( i = 0; i < MSI_MAX_PROPS; i++ )
if( property[i].vt != VT_EMPTY )
n++;
return n;
}
/* FIXME: doesn't deal with endian conversion */
static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz )
{
UINT type;
DWORD i;
int size;
PROPERTY_DATA *propdata;
PROPVARIANT *property;
PROPERTYIDOFFSET *idofs;
PROPERTYSECTIONHEADER *section_hdr;
section_hdr = (PROPERTYSECTIONHEADER*) &data[0];
idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE];
/* now set all the properties */
for( i = 0; i < section_hdr->cProperties; i++ )
{
type = get_type( idofs[i].propid );
if( type == VT_EMPTY )
{
ERR("propid %d has unknown type\n", idofs[i].propid);
break;
}
propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ];
/* check the type is the same as we expect */
if( type != propdata->type )
{
ERR("wrong type %d != %d\n", type, propdata->type);
break;
}
/* check we don't run off the end of the data */
size = sz - idofs[i].dwOffset - sizeof(DWORD);
if( sizeof(DWORD) > size ||
( type == VT_FILETIME && sizeof(FILETIME) > size ) ||
( type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) )
{
ERR("not enough data\n");
break;
}
if( idofs[i].propid >= MSI_MAX_PROPS )
{
ERR("Unknown property ID %d\n", idofs[i].propid );
break;
}
property = &prop[ idofs[i].propid ];
property->vt = type;
if( type == VT_LPSTR )
{
LPSTR str = msi_alloc( propdata->u.str.len );
memcpy( str, propdata->u.str.str, propdata->u.str.len );
str[ propdata->u.str.len - 1 ] = 0;
property->u.pszVal = str;
}
else if( type == VT_FILETIME )
property->u.filetime = propdata->u.ft;
else if( type == VT_I2 )
property->u.iVal = propdata->u.i2;
else if( type == VT_I4 )
property->u.lVal = propdata->u.i4;
}
}
static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm )
{
UINT ret = ERROR_FUNCTION_FAILED;
PROPERTYSETHEADER set_hdr;
FORMATIDOFFSET format_hdr;
PROPERTYSECTIONHEADER section_hdr;
LPBYTE data = NULL;
LARGE_INTEGER ofs;
ULONG count, sz;
HRESULT r;
TRACE("%p %p\n", si, stm);
/* read the header */
sz = sizeof set_hdr;
r = IStream_Read( stm, &set_hdr, sz, &count );
if( FAILED(r) || count != sz )
return ret;
if( set_hdr.wByteOrder != 0xfffe )
{
ERR("property set not big-endian %04X\n", set_hdr.wByteOrder);
return ret;
}
sz = sizeof format_hdr;
r = IStream_Read( stm, &format_hdr, sz, &count );
if( FAILED(r) || count != sz )
return ret;
/* check the format id is correct */
if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) )
return ret;
/* seek to the location of the section */
ofs.QuadPart = format_hdr.dwOffset;
r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL );
if( FAILED(r) )
return ret;
/* read the section itself */
sz = SECT_HDR_SIZE;
r = IStream_Read( stm, §ion_hdr, sz, &count );
if( FAILED(r) || count != sz )
return ret;
if( section_hdr.cProperties > MSI_MAX_PROPS )
{
ERR("too many properties %d\n", section_hdr.cProperties);
return ret;
}
data = msi_alloc( section_hdr.cbSection);
if( !data )
return ret;
memcpy( data, §ion_hdr, SECT_HDR_SIZE );
/* read all the data in one go */
sz = section_hdr.cbSection - SECT_HDR_SIZE;
r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count );
if( SUCCEEDED(r) && count == sz )
read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE );
else
ERR("failed to read properties %d %d\n", count, sz);
msi_free( data );
return ret;
}
static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val )
{
if( data )
{
data[ofs++] = val&0xff;
data[ofs++] = (val>>8)&0xff;
data[ofs++] = (val>>16)&0xff;
data[ofs++] = (val>>24)&0xff;
}
return 4;
}
static DWORD write_filetime( LPBYTE data, DWORD ofs, const FILETIME *ft )
{
write_dword( data, ofs, ft->dwLowDateTime );
write_dword( data, ofs + 4, ft->dwHighDateTime );
return 8;
}
static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str )
{
DWORD len = lstrlenA( str ) + 1;
write_dword( data, ofs, len );
if( data )
memcpy( &data[ofs + 4], str, len );
return (7 + len) & ~3;
}
static UINT write_property_to_data( const PROPVARIANT *prop, LPBYTE data )
{
DWORD sz = 0;
if( prop->vt == VT_EMPTY )
return sz;
/* add the type */
sz += write_dword( data, sz, prop->vt );
switch( prop->vt )
{
case VT_I2:
sz += write_dword( data, sz, prop->u.iVal );
break;
case VT_I4:
sz += write_dword( data, sz, prop->u.lVal );
break;
case VT_FILETIME:
sz += write_filetime( data, sz, &prop->u.filetime );
break;
case VT_LPSTR:
sz += write_string( data, sz, prop->u.pszVal );
break;
}
return sz;
}
static UINT save_summary_info( const MSISUMMARYINFO * si, IStream *stm )
{
UINT ret = ERROR_FUNCTION_FAILED;
PROPERTYSETHEADER set_hdr;
FORMATIDOFFSET format_hdr;
PROPERTYSECTIONHEADER section_hdr;
PROPERTYIDOFFSET idofs[MSI_MAX_PROPS];
LPBYTE data = NULL;
ULONG count, sz;
HRESULT r;
int i, n;
/* write the header */
sz = sizeof set_hdr;
memset( &set_hdr, 0, sz );
set_hdr.wByteOrder = 0xfffe;
set_hdr.wFormat = 0;
set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */
/* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */
set_hdr.reserved = 1;
r = IStream_Write( stm, &set_hdr, sz, &count );
if( FAILED(r) || count != sz )
return ret;
/* write the format header */
sz = sizeof format_hdr;
memcpy( &format_hdr.fmtid, &FMTID_SummaryInformation, sizeof (FMTID) );
format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr;
r = IStream_Write( stm, &format_hdr, sz, &count );
if( FAILED(r) || count != sz )
return ret;
/* add up how much space the data will take and calculate the offsets */
section_hdr.cbSection = sizeof section_hdr;
section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]);
section_hdr.cProperties = 0;
n = 0;
for( i = 0; i < MSI_MAX_PROPS; i++ )
{
sz = write_property_to_data( &si->property[i], NULL );
if( !sz )
continue;
idofs[ section_hdr.cProperties ].propid = i;
idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection;
section_hdr.cProperties++;
section_hdr.cbSection += sz;
}
data = msi_alloc_zero( section_hdr.cbSection );
sz = 0;
memcpy( &data[sz], §ion_hdr, sizeof section_hdr );
sz += sizeof section_hdr;
memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] );
sz += section_hdr.cProperties * sizeof idofs[0];
/* write out the data */
for( i = 0; i < MSI_MAX_PROPS; i++ )
sz += write_property_to_data( &si->property[i], &data[sz] );
r = IStream_Write( stm, data, sz, &count );
msi_free( data );
if( FAILED(r) || count != sz )
return ret;
return ERROR_SUCCESS;
}
MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount )
{
IStream *stm = NULL;
MSISUMMARYINFO *si;
DWORD grfMode;
HRESULT r;
TRACE("%p %d\n", stg, uiUpdateCount );
si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO,
sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
if( !si )
return si;
memset( &si->property, 0, sizeof si->property );
si->update_count = uiUpdateCount;
IStorage_AddRef( stg );
si->storage = stg;
/* read the stream... if we fail, we'll start with an empty property set */
grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
r = IStorage_OpenStream( si->storage, szSumInfo, 0, grfMode, 0, &stm );
if( SUCCEEDED(r) )
{
load_summary_info( si, stm );
IStream_Release( stm );
}
return si;
}
UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase,
LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle )
{
MSISUMMARYINFO *si;
MSIDATABASE *db;
UINT ret = ERROR_FUNCTION_FAILED;
TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase),
uiUpdateCount, pHandle);
if( !pHandle )
return ERROR_INVALID_PARAMETER;
if( szDatabase )
{
ret = MSI_OpenDatabaseW( szDatabase, NULL, &db );
if( ret != ERROR_SUCCESS )
return ret;
}
else
{
db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
if( !db )
return ERROR_INVALID_PARAMETER;
}
si = MSI_GetSummaryInformationW( db->storage, uiUpdateCount );
if (si)
{
*pHandle = alloc_msihandle( &si->hdr );
if( *pHandle )
ret = ERROR_SUCCESS;
else
ret = ERROR_NOT_ENOUGH_MEMORY;
msiobj_release( &si->hdr );
}
if( db )
msiobj_release( &db->hdr );
return ret;
}
UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase,
LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle)
{
LPWSTR szwDatabase = NULL;
UINT ret;
TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase),
uiUpdateCount, pHandle);
if( szDatabase )
{
szwDatabase = strdupAtoW( szDatabase );
if( !szwDatabase )
return ERROR_FUNCTION_FAILED;
}
ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
msi_free( szwDatabase );
return ret;
}
UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
{
MSISUMMARYINFO *si;
TRACE("%ld %p\n", hSummaryInfo, pCount);
si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
if( !si )
return ERROR_INVALID_HANDLE;
if( pCount )
*pCount = get_property_count( si->property );
msiobj_release( &si->hdr );
return ERROR_SUCCESS;
}
static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType,
INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
{
MSISUMMARYINFO *si;
PROPVARIANT *prop;
UINT ret = ERROR_SUCCESS;
TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
piValue, pftValue, str, pcchValueBuf);
if ( uiProperty >= MSI_MAX_PROPS )
{
if (puiDataType) *puiDataType = VT_EMPTY;
return ERROR_UNKNOWN_PROPERTY;
}
si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
if( !si )
return ERROR_INVALID_HANDLE;
prop = &si->property[uiProperty];
if( puiDataType )
*puiDataType = prop->vt;
switch( prop->vt )
{
case VT_I2:
if( piValue )
*piValue = prop->u.iVal;
break;
case VT_I4:
if( piValue )
*piValue = prop->u.lVal;
break;
case VT_LPSTR:
if( pcchValueBuf )
{
DWORD len = 0;
if( str->unicode )
{
len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1,
str->str.w, *pcchValueBuf );
len--;
}
else
{
len = lstrlenA( prop->u.pszVal );
if( str->str.a )
lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf );
}
if (len >= *pcchValueBuf)
ret = ERROR_MORE_DATA;
*pcchValueBuf = len;
}
break;
case VT_FILETIME:
if( pftValue )
memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) );
break;
case VT_EMPTY:
break;
default:
FIXME("Unknown property variant type\n");
break;
}
msiobj_release( &si->hdr );
return ret;
}
LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
{
PROPVARIANT *prop;
if ( uiProperty >= MSI_MAX_PROPS )
return NULL;
prop = &si->property[uiProperty];
if( prop->vt != VT_LPSTR )
return NULL;
return strdupAtoW( prop->u.pszVal );
}
LPWSTR msi_get_suminfo_product( IStorage *stg )
{
MSISUMMARYINFO *si;
LPWSTR prod;
si = MSI_GetSummaryInformationW( stg, 0 );
if (!si)
{
ERR("no summary information!\n");
return NULL;
}
prod = msi_suminfo_dup_string( si, PID_REVNUMBER );
msiobj_release( &si->hdr );
return prod;
}
UINT WINAPI MsiSummaryInfoGetPropertyA(
MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
{
awstring str;
TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
piValue, pftValue, szValueBuf, pcchValueBuf );
str.unicode = FALSE;
str.str.a = szValueBuf;
return get_prop( handle, uiProperty, puiDataType, piValue,
pftValue, &str, pcchValueBuf );
}
UINT WINAPI MsiSummaryInfoGetPropertyW(
MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
{
awstring str;
TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
piValue, pftValue, szValueBuf, pcchValueBuf );
str.unicode = TRUE;
str.str.w = szValueBuf;
return get_prop( handle, uiProperty, puiDataType, piValue,
pftValue, &str, pcchValueBuf );
}
static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType,
INT iValue, FILETIME* pftValue, awcstring *str )
{
MSISUMMARYINFO *si;
PROPVARIANT *prop;
UINT type, len, ret = ERROR_SUCCESS;
TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType,
iValue, pftValue, str );
type = get_type( uiProperty );
if( type == VT_EMPTY || type != uiDataType )
return ERROR_DATATYPE_MISMATCH;
if( uiDataType == VT_LPSTR && !str->str.w )
return ERROR_INVALID_PARAMETER;
if( uiDataType == VT_FILETIME && !pftValue )
return ERROR_INVALID_PARAMETER;
si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
if( !si )
return ERROR_INVALID_HANDLE;
prop = &si->property[uiProperty];
if( prop->vt == VT_EMPTY )
{
if( !si->update_count )
{
ret = ERROR_FUNCTION_FAILED;
goto end;
}
si->update_count--;
}
else if( prop->vt != type )
goto end;
free_prop( prop );
prop->vt = type;
switch( type )
{
case VT_I4:
prop->u.lVal = iValue;
break;
case VT_I2:
prop->u.iVal = iValue;
break;
case VT_FILETIME:
memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime );
break;
case VT_LPSTR:
if( str->unicode )
{
len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
NULL, 0, NULL, NULL );
prop->u.pszVal = msi_alloc( len );
WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
prop->u.pszVal, len, NULL, NULL );
}
else
{
len = lstrlenA( str->str.a ) + 1;
prop->u.pszVal = msi_alloc( len );
lstrcpyA( prop->u.pszVal, str->str.a );
}
break;
}
end:
msiobj_release( &si->hdr );
return ret;
}
UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty,
UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue )
{
awcstring str;
TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
iValue, pftValue, debugstr_w(szValue) );
str.unicode = TRUE;
str.str.w = szValue;
return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
}
UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty,
UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue )
{
awcstring str;
TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
iValue, pftValue, debugstr_a(szValue) );
str.unicode = FALSE;
str.str.a = szValue;
return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
}
UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
{
IStream *stm = NULL;
MSISUMMARYINFO *si;
DWORD grfMode;
HRESULT r;
UINT ret = ERROR_FUNCTION_FAILED;
TRACE("%ld\n", handle );
si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
if( !si )
return ERROR_INVALID_HANDLE;
grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
r = IStorage_CreateStream( si->storage, szSumInfo, grfMode, 0, 0, &stm );
if( SUCCEEDED(r) )
{
ret = save_summary_info( si, stm );
IStream_Release( stm );
}
msiobj_release( &si->hdr );
return ret;
}
--- NEW FILE: table.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002-2005 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
[...2406 lines suppressed...]
{
MSITRANSFORM *t;
t = msi_alloc( sizeof *t );
t->stg = stg;
IStorage_AddRef( stg );
list_add_tail( &db->transforms, &t->entry );
}
void msi_free_transforms( MSIDATABASE *db )
{
while( !list_empty( &db->transforms ) )
{
MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
MSITRANSFORM, entry );
list_remove( &t->entry );
IStorage_Release( t->stg );
msi_free( t );
}
}
--- NEW FILE: tokenize.c ---
/*
** 2001 September 15
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** An tokenizer for SQL
**
** This file contains C code that splits an SQL input string up into
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "wine/unicode.h"
#include "query.h"
#include "sql.tab.h"
/*
** All the keywords of the SQL language are stored as in a hash
** table composed of instances of the following structure.
*/
typedef struct Keyword Keyword;
struct Keyword {
const WCHAR *zName; /* The keyword name */
int tokenType; /* The token value for this keyword */
};
#define MAX_TOKEN_LEN 11
static const WCHAR ADD_W[] = { 'A','D','D',0 };
static const WCHAR ALTER_W[] = { 'A','L','T','E','R',0 };
static const WCHAR AND_W[] = { 'A','N','D',0 };
static const WCHAR BY_W[] = { 'B','Y',0 };
static const WCHAR CHAR_W[] = { 'C','H','A','R',0 };
static const WCHAR CHARACTER_W[] = { 'C','H','A','R','A','C','T','E','R',0 };
static const WCHAR CREATE_W[] = { 'C','R','E','A','T','E',0 };
static const WCHAR DELETE_W[] = { 'D','E','L','E','T','E',0 };
static const WCHAR DISTINCT_W[] = { 'D','I','S','T','I','N','C','T',0 };
static const WCHAR FREE_W[] = { 'F','R','E','E',0 };
static const WCHAR FROM_W[] = { 'F','R','O','M',0 };
static const WCHAR HOLD_W[] = { 'H','O','L','D',0 };
static const WCHAR INSERT_W[] = { 'I','N','S','E','R','T',0 };
static const WCHAR INT_W[] = { 'I','N','T',0 };
static const WCHAR INTEGER_W[] = { 'I','N','T','E','G','E','R',0 };
static const WCHAR INTO_W[] = { 'I','N','T','O',0 };
static const WCHAR IS_W[] = { 'I','S',0 };
static const WCHAR KEY_W[] = { 'K','E','Y',0 };
static const WCHAR LIKE_W[] = { 'L','I','K','E',0 };
static const WCHAR LOCALIZABLE_W[] = { 'L','O','C','A','L','I','Z','A','B','L','E',0 };
static const WCHAR LONG_W[] = { 'L','O','N','G',0 };
static const WCHAR LONGCHAR_W[] = { 'L','O','N','G','C','H','A','R',0 };
static const WCHAR NOT_W[] = { 'N','O','T',0 };
static const WCHAR NULL_W[] = { 'N','U','L','L',0 };
static const WCHAR OBJECT_W[] = { 'O','B','J','E','C','T',0 };
static const WCHAR OR_W[] = { 'O','R',0 };
static const WCHAR ORDER_W[] = { 'O','R','D','E','R',0 };
static const WCHAR PRIMARY_W[] = { 'P','R','I','M','A','R','Y',0 };
static const WCHAR SELECT_W[] = { 'S','E','L','E','C','T',0 };
static const WCHAR SET_W[] = { 'S','E','T',0 };
static const WCHAR SHORT_W[] = { 'S','H','O','R','T',0 };
static const WCHAR TABLE_W[] = { 'T','A','B','L','E',0 };
static const WCHAR TEMPORARY_W[] = { 'T','E','M','P','O','R','A','R','Y',0 };
static const WCHAR UPDATE_W[] = { 'U','P','D','A','T','E',0 };
static const WCHAR VALUES_W[] = { 'V','A','L','U','E','S',0 };
static const WCHAR WHERE_W[] = { 'W','H','E','R','E',0 };
/*
** These are the keywords
*/
static const Keyword aKeywordTable[] = {
{ ADD_W, TK_ADD },
{ ALTER_W, TK_ALTER },
{ AND_W, TK_AND },
{ BY_W, TK_BY },
{ CHAR_W, TK_CHAR },
{ CHARACTER_W, TK_CHAR },
{ CREATE_W, TK_CREATE },
{ DELETE_W, TK_DELETE },
{ DISTINCT_W, TK_DISTINCT },
{ FREE_W, TK_FREE },
{ FROM_W, TK_FROM },
{ HOLD_W, TK_HOLD },
{ INSERT_W, TK_INSERT },
{ INT_W, TK_INT },
{ INTEGER_W, TK_INT },
{ INTO_W, TK_INTO },
{ IS_W, TK_IS },
{ KEY_W, TK_KEY },
{ LIKE_W, TK_LIKE },
{ LOCALIZABLE_W, TK_LOCALIZABLE },
{ LONG_W, TK_LONG },
{ LONGCHAR_W, TK_LONGCHAR },
{ NOT_W, TK_NOT },
{ NULL_W, TK_NULL },
{ OBJECT_W, TK_OBJECT },
{ OR_W, TK_OR },
{ ORDER_W, TK_ORDER },
{ PRIMARY_W, TK_PRIMARY },
{ SELECT_W, TK_SELECT },
{ SET_W, TK_SET },
{ SHORT_W, TK_SHORT },
{ TABLE_W, TK_TABLE },
{ TEMPORARY_W, TK_TEMPORARY },
{ UPDATE_W, TK_UPDATE },
{ VALUES_W, TK_VALUES },
{ WHERE_W, TK_WHERE },
};
#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) )
/*
** Comparison function for binary search.
*/
static int compKeyword(const void *m1, const void *m2){
const Keyword *k1 = m1, *k2 = m2;
return strcmpiW( k1->zName, k2->zName );
}
/*
** This function looks up an identifier to determine if it is a
** keyword. If it is a keyword, the token code of that keyword is
** returned. If the input is not a keyword, TK_ID is returned.
*/
static int sqliteKeywordCode(const WCHAR *z, int n){
WCHAR str[MAX_TOKEN_LEN+1];
Keyword key, *r;
if( n>MAX_TOKEN_LEN )
return TK_ID;
memcpy( str, z, n*sizeof (WCHAR) );
str[n] = 0;
key.tokenType = 0;
key.zName = str;
r = bsearch( &key, aKeywordTable, KEYWORD_COUNT, sizeof (Keyword), compKeyword );
if( r )
return r->tokenType;
return TK_ID;
}
/*
** If X is a character that can be used in an identifier then
** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0.
**
** In this implementation, an identifier can be a string of
** alphabetic characters, digits, and "_" plus any character
** with the high-order bit set. The latter rule means that
** any sequence of UTF-8 characters or characters taken from
** an extended ISO8859 character set can form an identifier.
*/
static const char isIdChar[] = {
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
};
/*
** Return the length of the token that begins at z[0]. Return
** -1 if the token is (or might be) incomplete. Store the token
** type in *tokenType before returning.
*/
int sqliteGetToken(const WCHAR *z, int *tokenType){
int i;
switch( *z ){
case ' ': case '\t': case '\n': case '\f': case '\r': {
for(i=1; isspace(z[i]); i++){}
*tokenType = TK_SPACE;
return i;
}
case '-': {
if( z[1]==0 ) return -1;
*tokenType = TK_MINUS;
return 1;
}
case '(':
*tokenType = TK_LP;
return 1;
case ')':
*tokenType = TK_RP;
return 1;
case '*':
*tokenType = TK_STAR;
return 1;
case '=':
*tokenType = TK_EQ;
return 1;
case '<': {
if( z[1]=='=' ){
*tokenType = TK_LE;
return 2;
}else if( z[1]=='>' ){
*tokenType = TK_NE;
return 2;
}else{
*tokenType = TK_LT;
return 1;
}
}
case '>': {
if( z[1]=='=' ){
*tokenType = TK_GE;
return 2;
}else{
*tokenType = TK_GT;
return 1;
}
}
case '!': {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
return 2;
}else{
*tokenType = TK_NE;
return 2;
}
}
case '?':
*tokenType = TK_WILDCARD;
return 1;
case ',':
*tokenType = TK_COMMA;
return 1;
case '`': case '\'': {
int delim = z[0];
for(i=1; z[i]; i++){
if( z[i]==delim ){
if( z[i+1]==delim ){
i++;
}else{
break;
}
}
}
if( z[i] ) i++;
if( delim == '`' )
*tokenType = TK_ID;
else
*tokenType = TK_STRING;
return i;
}
case '.': {
if( !isdigit(z[1]) ){
*tokenType = TK_DOT;
return 1;
}
/* Fall thru into the next case */
}
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
*tokenType = TK_INTEGER;
for(i=1; isdigit(z[i]); i++){}
return i;
}
case '[': {
for(i=1; z[i] && z[i-1]!=']'; i++){}
*tokenType = TK_ID;
return i;
}
default: {
if( !isIdChar[*z] ){
break;
}
for(i=1; isIdChar[z[i]]; i++){}
*tokenType = sqliteKeywordCode(z, i);
return i;
}
}
*tokenType = TK_ILLEGAL;
return 1;
}
--- NEW FILE: update.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2004 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSIUPDATEVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *wv;
column_info *vals;
} MSIUPDATEVIEW;
static UINT UPDATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
TRACE("%p %d %d %p\n", uv, row, col, val );
return ERROR_FUNCTION_FAILED;
}
static UINT UPDATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
UINT i, r, col_count = 0, row_count = 0;
MSIRECORD *values = NULL;
MSIVIEW *wv;
TRACE("%p %p\n", uv, record );
wv = uv->wv;
if( !wv )
return ERROR_FUNCTION_FAILED;
r = wv->ops->execute( wv, 0 );
TRACE("tv execute returned %x\n", r);
if( r )
return r;
r = wv->ops->get_dimensions( wv, &row_count, &col_count );
if( r )
return r;
values = msi_query_merge_record( col_count, uv->vals, record );
if (!values)
return ERROR_FUNCTION_FAILED;
for ( i=0; i<row_count; i++ )
{
r = wv->ops->set_row( wv, i, values, (1 << col_count) - 1 );
if (r != ERROR_SUCCESS)
break;
}
msiobj_release( &values->hdr );
return r;
}
static UINT UPDATE_close( struct tagMSIVIEW *view )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
MSIVIEW *wv;
TRACE("%p\n", uv);
wv = uv->wv;
if( !wv )
return ERROR_FUNCTION_FAILED;
return wv->ops->close( wv );
}
static UINT UPDATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
MSIVIEW *wv;
TRACE("%p %p %p\n", uv, rows, cols );
wv = uv->wv;
if( !wv )
return ERROR_FUNCTION_FAILED;
return wv->ops->get_dimensions( wv, rows, cols );
}
static UINT UPDATE_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
MSIVIEW *wv;
TRACE("%p %d %p %p\n", uv, n, name, type );
wv = uv->wv;
if( !wv )
return ERROR_FUNCTION_FAILED;
return wv->ops->get_column_info( wv, n, name, type );
}
static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
TRACE("%p %d %p\n", uv, eModifyMode, rec );
return ERROR_FUNCTION_FAILED;
}
static UINT UPDATE_delete( struct tagMSIVIEW *view )
{
MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
MSIVIEW *wv;
TRACE("%p\n", uv );
wv = uv->wv;
if( wv )
wv->ops->delete( wv );
msiobj_release( &uv->db->hdr );
msi_free( uv );
return ERROR_SUCCESS;
}
static UINT UPDATE_find_matching_rows( struct tagMSIVIEW *view, UINT col, UINT val, UINT *row, MSIITERHANDLE *handle )
{
TRACE("%p %d %d %p\n", view, col, val, *handle );
return ERROR_FUNCTION_FAILED;
}
static const MSIVIEWOPS update_ops =
{
UPDATE_fetch_int,
NULL,
NULL,
NULL,
NULL,
UPDATE_execute,
UPDATE_close,
UPDATE_get_dimensions,
UPDATE_get_column_info,
UPDATE_modify,
UPDATE_delete,
UPDATE_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
column_info *columns, struct expr *expr )
{
MSIUPDATEVIEW *uv = NULL;
UINT r;
MSIVIEW *tv = NULL, *sv = NULL, *wv = NULL;
TRACE("%p\n", uv );
r = TABLE_CreateView( db, table, &tv );
if( r != ERROR_SUCCESS )
return r;
if (expr)
{
/* add conditions first */
r = WHERE_CreateView( db, &wv, tv, expr );
if( r != ERROR_SUCCESS )
{
tv->ops->delete( tv );
return r;
}
}
else
wv = tv;
/* then select the columns we want */
r = SELECT_CreateView( db, &sv, wv, columns );
if( r != ERROR_SUCCESS )
{
wv->ops->delete( wv );
return r;
}
uv = msi_alloc_zero( sizeof *uv );
if( !uv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
uv->view.ops = &update_ops;
msiobj_addref( &db->hdr );
uv->db = db;
uv->vals = columns;
uv->wv = sv;
*view = (MSIVIEW*) uv;
return ERROR_SUCCESS;
}
--- NEW FILE: upgrade.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2005 Aric Stewart for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* Actions focused on in this module
*
* FindRelatedProducts
* MigrateFeatureStates (TODO)
* RemoveExistingProducts (TODO)
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "wine/debug.h"
#include "wine-lgpl/msidefs.h"
#include "msipriv.h"
#include "winuser.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
extern const WCHAR szFindRelatedProducts[];
extern const WCHAR szMigrateFeatureStates[];
extern const WCHAR szRemoveExistingProducts[];
static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
{
DWORD langdword;
if (!lang2 || lang2[0]==0)
return TRUE;
langdword = atoiW(lang2);
if (attributes & msidbUpgradeAttributesLanguagesExclusive)
return (lang1 != langdword);
else
return (lang1 == langdword);
}
static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property,
LPCWSTR productid)
{
LPWSTR prop;
LPWSTR newprop;
DWORD len;
static const WCHAR separator[] = {';',0};
prop = msi_dup_property(package, action_property );
if (prop)
len = strlenW(prop);
else
len = 0;
/*separator*/
len ++;
len += strlenW(productid);
/*null*/
len++;
newprop = msi_alloc( len*sizeof(WCHAR) );
if (prop)
{
strcpyW(newprop,prop);
strcatW(newprop,separator);
}
else
newprop[0] = 0;
strcatW(newprop,productid);
MSI_SetPropertyW(package, action_property, newprop);
TRACE("Found Related Product... %s now %s\n",debugstr_w(action_property),
debugstr_w(newprop));
msi_free( prop );
msi_free( newprop );
}
static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
{
MSIPACKAGE *package = (MSIPACKAGE*)param;
WCHAR product[GUID_SIZE];
DWORD index = 0;
DWORD attributes = 0;
DWORD sz = GUID_SIZE;
LPCWSTR upgrade_code;
HKEY hkey = 0;
UINT rc = ERROR_SUCCESS;
MSIRECORD *uirow;
upgrade_code = MSI_RecordGetString(rec,1);
rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
if (rc != ERROR_SUCCESS)
return ERROR_SUCCESS;
uirow = MSI_CreateRecord(1);
attributes = MSI_RecordGetInteger(rec,5);
while (rc == ERROR_SUCCESS)
{
rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
TRACE("Looking at (%i) %s\n",index,debugstr_w(product));
if (rc == ERROR_SUCCESS)
{
WCHAR productid[GUID_SIZE];
LPCWSTR ver;
LPCWSTR language;
LPCWSTR action_property;
DWORD check = 0x00000000;
DWORD comp_ver = 0x00000000;
DWORD sz = 0x100;
HKEY hukey;
INT r;
unsquash_guid(product,productid);
rc = MSIREG_OpenUserProductsKey(productid, &hukey, FALSE);
if (rc != ERROR_SUCCESS)
{
rc = ERROR_SUCCESS;
index ++;
continue;
}
sz = sizeof(DWORD);
RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL,
(LPBYTE)&check, &sz);
/* check min */
ver = MSI_RecordGetString(rec,2);
comp_ver = msi_version_str_to_dword(ver);
r = check - comp_ver;
if (r < 0 || (r == 0 && !(attributes &
msidbUpgradeAttributesVersionMinInclusive)))
{
RegCloseKey(hukey);
index ++;
continue;
}
/* check max */
ver = MSI_RecordGetString(rec,3);
comp_ver = msi_version_str_to_dword(ver);
r = check - comp_ver;
if (r > 0 || (r == 0 && !(attributes &
msidbUpgradeAttributesVersionMaxInclusive)))
{
RegCloseKey(hukey);
index ++;
continue;
}
/* check language*/
sz = sizeof(DWORD);
RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL,
(LPBYTE)&check, &sz);
RegCloseKey(hukey);
language = MSI_RecordGetString(rec,4);
TRACE("Checking languages %x and %s\n", check,
debugstr_w(language));
if (!check_language(check, language, attributes))
{
index ++;
continue;
}
action_property = MSI_RecordGetString(rec,7);
append_productcode(package,action_property,productid);
ui_actiondata(package,szFindRelatedProducts,uirow);
}
index ++;
}
RegCloseKey(hkey);
msiobj_release( &uirow->hdr);
return ERROR_SUCCESS;
}
UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
{
static const WCHAR Query[] =
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',
' ','`','U','p','g','r','a','d','e','`',0};
UINT rc = ERROR_SUCCESS;
MSIQUERY *view;
if (check_unique_action(package,szFindRelatedProducts))
{
TRACE("Skipping FindRelatedProducts action: already done on client side\n");
return ERROR_SUCCESS;
}
else
register_unique_action(package,szFindRelatedProducts);
rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
if (rc != ERROR_SUCCESS)
return ERROR_SUCCESS;
rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
msiobj_release(&view->hdr);
return rc;
}
--- NEW FILE: version.rc ---
/*
* Copyright (c) 2004 Christian Costa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define WINE_FILEDESCRIPTION_STR "Wine MSI dll"
#define WINE_FILENAME_STR "msi.dll"
#define WINE_FILEVERSION 3,1,4000,2435
#define WINE_FILEVERSION_STR "3.1.4000.2435"
#define WINE_PRODUCTVERSION 3,1,4000,2435
#define WINE_PRODUCTVERSION_STR "3.1.4000.2435"
#include "wine/wine_common_ver.rc"
--- NEW FILE: where.c ---
/*
* Implementation of the Microsoft Installer (msi.dll)
*
* Copyright 2002 Mike McCormack for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "wine-lgpl/msi.h"
#include "wine-lgpl/msiquery.h"
#include "objbase.h"
#include "objidl.h"
#include "msipriv.h"
#include "winnls.h"
#include "query.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
/* below is the query interface to a table */
typedef struct tagMSIWHEREVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSIVIEW *table;
UINT row_count;
UINT *reorder;
struct expr *cond;
} MSIWHEREVIEW;
static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %d %d %p\n", wv, row, col, val );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
if( row > wv->row_count )
return ERROR_NO_MORE_ITEMS;
row = wv->reorder[ row ];
return wv->table->ops->fetch_int( wv->table, row, col, val );
}
static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %d %d %p\n", wv, row, col, stm );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
if( row > wv->row_count )
return ERROR_NO_MORE_ITEMS;
row = wv->reorder[ row ];
return wv->table->ops->fetch_stream( wv->table, row, col, stm );
}
static UINT WHERE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %d %p %08x\n", wv, row, rec, mask );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
if( row > wv->row_count )
return ERROR_NO_MORE_ITEMS;
row = wv->reorder[ row ];
return wv->table->ops->set_row( wv->table, row, rec, mask );
}
static INT INT_evaluate_binary( INT lval, UINT op, INT rval )
{
switch( op )
{
case OP_EQ:
return ( lval == rval );
case OP_AND:
return ( lval && rval );
case OP_OR:
return ( lval || rval );
case OP_GT:
return ( lval > rval );
case OP_LT:
return ( lval < rval );
case OP_LE:
return ( lval <= rval );
case OP_GE:
return ( lval >= rval );
case OP_NE:
return ( lval != rval );
default:
ERR("Unknown operator %d\n", op );
}
return 0;
}
static INT INT_evaluate_unary( INT lval, UINT op )
{
switch( op )
{
case OP_ISNULL:
return ( !lval );
case OP_NOTNULL:
return ( lval );
default:
ERR("Unknown operator %d\n", op );
}
return 0;
}
static const WCHAR *STRING_evaluate( const string_table *st,
MSIVIEW *table, UINT row, const struct expr *expr, const MSIRECORD *record )
{
UINT val = 0, r;
switch( expr->type )
{
case EXPR_COL_NUMBER_STRING:
r = table->ops->fetch_int( table, row, expr->u.col_number, &val );
if( r != ERROR_SUCCESS )
return NULL;
return msi_string_lookup_id( st, val );
case EXPR_SVAL:
return expr->u.sval;
case EXPR_WILDCARD:
return MSI_RecordGetString( record, 1 );
default:
ERR("Invalid expression type\n");
break;
}
return NULL;
}
static UINT STRCMP_Evaluate( const string_table *st, MSIVIEW *table, UINT row,
const struct expr *cond, INT *val, const MSIRECORD *record )
{
int sr;
const WCHAR *l_str, *r_str;
l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record );
r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record );
if( l_str == r_str )
sr = 0;
else if( l_str && ! r_str )
sr = 1;
else if( r_str && ! l_str )
sr = -1;
else
sr = lstrcmpW( l_str, r_str );
*val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
( cond->u.expr.op == OP_GT && ( sr > 0 ) );
return ERROR_SUCCESS;
}
static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row,
const struct expr *cond, INT *val, MSIRECORD *record )
{
UINT r, tval;
INT lval, rval;
if( !cond )
return ERROR_SUCCESS;
switch( cond->type )
{
case EXPR_COL_NUMBER:
r = table->ops->fetch_int( table, row, cond->u.col_number, &tval );
*val = tval - 0x8000;
return ERROR_SUCCESS;
case EXPR_COL_NUMBER32:
r = table->ops->fetch_int( table, row, cond->u.col_number, &tval );
*val = tval - 0x80000000;
return r;
case EXPR_UVAL:
*val = cond->u.uval;
return ERROR_SUCCESS;
case EXPR_COMPLEX:
r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record );
if( r != ERROR_SUCCESS )
return r;
r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record );
if( r != ERROR_SUCCESS )
return r;
*val = INT_evaluate_binary( lval, cond->u.expr.op, rval );
return ERROR_SUCCESS;
case EXPR_UNARY:
r = table->ops->fetch_int( table, row, cond->u.expr.left->u.col_number, &tval );
if( r != ERROR_SUCCESS )
return r;
*val = INT_evaluate_unary( tval, cond->u.expr.op );
return ERROR_SUCCESS;
case EXPR_STRCMP:
return STRCMP_Evaluate( db->strings, table, row, cond, val, record );
case EXPR_WILDCARD:
*val = MSI_RecordGetInteger( record, 1 );
return ERROR_SUCCESS;
default:
ERR("Invalid expression type\n");
break;
}
return ERROR_SUCCESS;
}
static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
UINT count = 0, r, i;
INT val;
MSIVIEW *table = wv->table;
TRACE("%p %p\n", wv, record);
if( !table )
return ERROR_FUNCTION_FAILED;
r = table->ops->execute( table, record );
if( r != ERROR_SUCCESS )
return r;
r = table->ops->get_dimensions( table, &count, NULL );
if( r != ERROR_SUCCESS )
return r;
msi_free( wv->reorder );
wv->reorder = msi_alloc( count*sizeof(UINT) );
if( !wv->reorder )
return ERROR_FUNCTION_FAILED;
wv->row_count = 0;
if (wv->cond->type == EXPR_STRCMP)
{
MSIITERHANDLE handle = NULL;
UINT row, value, col;
struct expr *col_cond = wv->cond->u.expr.left;
struct expr *val_cond = wv->cond->u.expr.right;
/* swap conditionals */
if (col_cond->type != EXPR_COL_NUMBER_STRING)
{
val_cond = wv->cond->u.expr.left;
col_cond = wv->cond->u.expr.right;
}
if ((col_cond->type == EXPR_COL_NUMBER_STRING) && (val_cond->type == EXPR_SVAL))
{
col = col_cond->u.col_number;
/* special case for "" - translate it into nil */
if (!val_cond->u.sval[0])
value = 0;
else
{
r = msi_string2idW(wv->db->strings, val_cond->u.sval, &value);
if (r != ERROR_SUCCESS)
{
TRACE("no id for %s, assuming it doesn't exist in the table\n", debugstr_w(wv->cond->u.expr.right->u.sval));
return ERROR_SUCCESS;
}
}
do
{
r = table->ops->find_matching_rows(table, col, value, &row, &handle);
if (r == ERROR_SUCCESS)
wv->reorder[ wv->row_count ++ ] = row;
} while (r == ERROR_SUCCESS);
if (r == ERROR_NO_MORE_ITEMS)
return ERROR_SUCCESS;
else
return r;
}
/* else fallback to slow case */
}
for( i=0; i<count; i++ )
{
val = 0;
r = WHERE_evaluate( wv->db, table, i, wv->cond, &val, record );
if( r != ERROR_SUCCESS )
return r;
if( val )
wv->reorder[ wv->row_count ++ ] = i;
}
return ERROR_SUCCESS;
}
static UINT WHERE_close( struct tagMSIVIEW *view )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p\n", wv );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
msi_free( wv->reorder );
wv->reorder = NULL;
return wv->table->ops->close( wv->table );
}
static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %p %p\n", wv, rows, cols );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
if( rows )
{
if( !wv->reorder )
return ERROR_FUNCTION_FAILED;
*rows = wv->row_count;
}
return wv->table->ops->get_dimensions( wv->table, NULL, cols );
}
static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
UINT n, LPWSTR *name, UINT *type )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %d %p %p\n", wv, n, name, type );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
return wv->table->ops->get_column_info( wv->table, n, name, type );
}
static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
MSIRECORD *rec )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p %d %p\n", wv, eModifyMode, rec );
if( !wv->table )
return ERROR_FUNCTION_FAILED;
return wv->table->ops->modify( wv->table, eModifyMode, rec );
}
static UINT WHERE_delete( struct tagMSIVIEW *view )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
TRACE("%p\n", wv );
if( wv->table )
wv->table->ops->delete( wv->table );
wv->table = 0;
msi_free( wv->reorder );
wv->reorder = NULL;
wv->row_count = 0;
msiobj_release( &wv->db->hdr );
msi_free( wv );
return ERROR_SUCCESS;
}
static UINT WHERE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
UINT val, UINT *row, MSIITERHANDLE *handle )
{
MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
UINT r;
TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
if( !wv->table )
return ERROR_FUNCTION_FAILED;
r = wv->table->ops->find_matching_rows( wv->table, col, val, row, handle );
if( *row > wv->row_count )
return ERROR_NO_MORE_ITEMS;
*row = wv->reorder[ *row ];
return r;
}
static const MSIVIEWOPS where_ops =
{
WHERE_fetch_int,
WHERE_fetch_stream,
WHERE_set_row,
NULL,
NULL,
WHERE_execute,
WHERE_close,
WHERE_get_dimensions,
WHERE_get_column_info,
WHERE_modify,
WHERE_delete,
WHERE_find_matching_rows,
NULL,
NULL,
NULL,
NULL,
};
static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
UINT *valid )
{
UINT r, val = 0;
switch( cond->type )
{
case EXPR_COLUMN:
r = VIEW_find_column( table, cond->u.column, &val );
if( r == ERROR_SUCCESS )
{
UINT type = 0;
r = table->ops->get_column_info( table, val, NULL, &type );
if( r == ERROR_SUCCESS )
{
if (type&MSITYPE_STRING)
cond->type = EXPR_COL_NUMBER_STRING;
else if ((type&0xff) == 4)
cond->type = EXPR_COL_NUMBER32;
else
cond->type = EXPR_COL_NUMBER;
cond->u.col_number = val;
*valid = 1;
}
else
*valid = 0;
}
else
{
*valid = 0;
ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
}
break;
case EXPR_COMPLEX:
r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
if( r != ERROR_SUCCESS )
return r;
if( !*valid )
return ERROR_SUCCESS;
r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid );
if( r != ERROR_SUCCESS )
return r;
/* check the type of the comparison */
if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
( cond->u.expr.right->type == EXPR_SVAL ) ||
( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
{
switch( cond->u.expr.op )
{
case OP_EQ:
case OP_GT:
case OP_LT:
break;
default:
*valid = FALSE;
return ERROR_INVALID_PARAMETER;
}
/* FIXME: check we're comparing a string to a column */
cond->type = EXPR_STRCMP;
}
break;
case EXPR_UNARY:
if ( cond->u.expr.left->type != EXPR_COLUMN )
{
*valid = FALSE;
return ERROR_INVALID_PARAMETER;
}
r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
if( r != ERROR_SUCCESS )
return r;
break;
case EXPR_IVAL:
*valid = 1;
cond->type = EXPR_UVAL;
cond->u.uval = cond->u.ival;
break;
case EXPR_WILDCARD:
*valid = 1;
break;
case EXPR_SVAL:
*valid = 1;
break;
default:
ERR("Invalid expression type\n");
*valid = 0;
break;
}
return ERROR_SUCCESS;
}
UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
struct expr *cond )
{
MSIWHEREVIEW *wv = NULL;
UINT count = 0, r, valid = 0;
TRACE("%p\n", table );
r = table->ops->get_dimensions( table, NULL, &count );
if( r != ERROR_SUCCESS )
{
ERR("can't get table dimensions\n");
return r;
}
if( cond )
{
r = WHERE_VerifyCondition( db, table, cond, &valid );
if( r != ERROR_SUCCESS )
return r;
if( !valid )
return ERROR_FUNCTION_FAILED;
}
wv = msi_alloc_zero( sizeof *wv );
if( !wv )
return ERROR_FUNCTION_FAILED;
/* fill the structure */
wv->view.ops = &where_ops;
msiobj_addref( &db->hdr );
wv->db = db;
wv->table = table;
wv->row_count = 0;
wv->reorder = NULL;
wv->cond = cond;
*view = (MSIVIEW*) wv;
return ERROR_SUCCESS;
}