msi: Add a test to show that patches are not committed to the local package database.
[wine] / dlls / msi / tests / patch.c
1 /*
2  * Copyright 2010 Hans Leidekker for CodeWeavers
3  *
4  * A test program for patching MSI products.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define _WIN32_MSI 300
22 #define COBJMACROS
23
24 #include <stdio.h>
25
26 #include <windows.h>
27 #include <msiquery.h>
28 #include <msidefs.h>
29 #include <msi.h>
30
31 #include "wine/test.h"
32
33 static UINT (WINAPI *pMsiApplyPatchA)( LPCSTR, LPCSTR, INSTALLTYPE, LPCSTR );
34 static UINT (WINAPI *pMsiGetPatchInfoExA)( LPCSTR, LPCSTR, LPCSTR, MSIINSTALLCONTEXT,
35                                            LPCSTR, LPSTR, DWORD * );
36
37 static const char *msifile = "winetest-patch.msi";
38 static const char *mspfile = "winetest-patch.msp";
39
40 static char CURR_DIR[MAX_PATH];
41 static char PROG_FILES_DIR[MAX_PATH];
42 static char COMMON_FILES_DIR[MAX_PATH];
43
44 /* msi database data */
45
46 static const char property_dat[] =
47     "Property\tValue\n"
48     "s72\tl0\n"
49     "Property\tProperty\n"
50     "Manufacturer\tWineHQ\n"
51     "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
52     "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
53     "ProductLanguage\t1033\n"
54     "ProductName\tmsitest\n"
55     "ProductVersion\t1.1.1\n";
56
57 static const char media_dat[] =
58     "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
59     "i2\ti4\tL64\tS255\tS32\tS72\n"
60     "Media\tDiskId\n"
61     "1\t1\t\t\tDISK1\t\n";
62
63 static const char file_dat[] =
64     "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
65     "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
66     "File\tFile\n"
67     "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
68
69 static const char directory_dat[] =
70     "Directory\tDirectory_Parent\tDefaultDir\n"
71     "s72\tS72\tl255\n"
72     "Directory\tDirectory\n"
73     "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
74     "ProgramFilesFolder\tTARGETDIR\t.\n"
75     "TARGETDIR\t\tSourceDir";
76
77 static const char component_dat[] =
78     "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
79     "s72\tS38\ts72\ti2\tS255\tS72\n"
80     "Component\tComponent\n"
81     "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
82
83 static const char feature_dat[] =
84     "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
85     "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
86     "Feature\tFeature\n"
87     "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
88
89 static const char feature_comp_dat[] =
90     "Feature_\tComponent_\n"
91     "s38\ts72\n"
92     "FeatureComponents\tFeature_\tComponent_\n"
93     "patch\tpatch\n";
94
95 static const char install_exec_seq_dat[] =
96     "Action\tCondition\tSequence\n"
97     "s72\tS255\tI2\n"
98     "InstallExecuteSequence\tAction\n"
99     "LaunchConditions\t\t100\n"
100     "CostInitialize\t\t800\n"
101     "FileCost\t\t900\n"
102     "CostFinalize\t\t1000\n"
103     "InstallValidate\t\t1400\n"
104     "InstallInitialize\t\t1500\n"
105     "ProcessComponents\t\t1600\n"
106     "RemoveFiles\t\t1700\n"
107     "InstallFiles\t\t2000\n"
108     "RegisterUser\t\t3000\n"
109     "RegisterProduct\t\t3100\n"
110     "PublishFeatures\t\t5100\n"
111     "PublishProduct\t\t5200\n"
112     "InstallFinalize\t\t6000\n";
113
114 struct msi_table
115 {
116     const char *filename;
117     const char *data;
118     int size;
119 };
120
121 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
122
123 static const struct msi_table tables[] =
124 {
125     ADD_TABLE( directory ),
126     ADD_TABLE( file ),
127     ADD_TABLE( component ),
128     ADD_TABLE( feature ),
129     ADD_TABLE( feature_comp ),
130     ADD_TABLE( property ),
131     ADD_TABLE( install_exec_seq ),
132     ADD_TABLE( media )
133 };
134
135 static void init_function_pointers( void )
136 {
137     HMODULE hmsi = GetModuleHandleA( "msi.dll" );
138
139 #define GET_PROC( mod, func ) \
140     p ## func = (void *)GetProcAddress( mod, #func ); \
141     if (!p ## func) \
142         trace( "GetProcAddress(%s) failed\n", #func );
143
144     GET_PROC( hmsi, MsiApplyPatchA );
145     GET_PROC( hmsi, MsiGetPatchInfoExA );
146 #undef GET_PROC
147 }
148
149 static BOOL get_program_files_dir( char *buf, char *buf2 )
150 {
151     HKEY hkey;
152     DWORD type, size;
153
154     if (RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
155         return FALSE;
156
157     size = MAX_PATH;
158     if (RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
159     {
160         RegCloseKey( hkey );
161         return FALSE;
162     }
163     size = MAX_PATH;
164     if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
165     {
166         RegCloseKey( hkey );
167         return FALSE;
168     }
169     RegCloseKey( hkey );
170     return TRUE;
171 }
172
173 static void create_file_data( const char *filename, const char *data, DWORD size )
174 {
175     HANDLE file;
176     DWORD written;
177
178     file = CreateFileA( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
179     if (file == INVALID_HANDLE_VALUE)
180         return;
181
182     WriteFile( file, data, strlen( data ), &written, NULL );
183     if (size)
184     {
185         SetFilePointer( file, size, NULL, FILE_BEGIN );
186         SetEndOfFile( file );
187     }
188     CloseHandle( file );
189 }
190
191 #define create_file( name, size ) create_file_data( name, name, size )
192
193 static BOOL delete_pf( const char *rel_path, BOOL is_file )
194 {
195     char path[MAX_PATH];
196
197     strcpy( path, PROG_FILES_DIR );
198     strcat( path, "\\" );
199     strcat( path, rel_path );
200
201     if (is_file)
202         return DeleteFileA( path );
203     else
204         return RemoveDirectoryA( path );
205 }
206
207 static DWORD get_pf_file_size( const char *filename )
208 {
209     char path[MAX_PATH];
210     HANDLE file;
211     DWORD size;
212
213     strcpy( path, PROG_FILES_DIR );
214     strcat( path, "\\");
215     strcat( path, filename );
216
217     file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
218     if (file == INVALID_HANDLE_VALUE)
219         return INVALID_FILE_SIZE;
220
221     size = GetFileSize( file, NULL );
222     CloseHandle( file );
223     return size;
224 }
225
226 static void write_file( const char *filename, const char *data, DWORD data_size )
227 {
228     DWORD size;
229     HANDLE file = CreateFile( filename, GENERIC_WRITE, 0, NULL,
230                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
231     WriteFile( file, data, data_size, &size, NULL );
232     CloseHandle( file );
233 }
234
235 static void set_suminfo( const char *filename )
236 {
237     UINT r;
238     MSIHANDLE hsi, hdb;
239
240     r = MsiOpenDatabaseA( filename, MSIDBOPEN_DIRECT, &hdb );
241     ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
242
243     r = MsiGetSummaryInformation( hdb, NULL, 7, &hsi );
244     ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
245
246     r = MsiSummaryInfoSetProperty( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
247     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
248
249     r = MsiSummaryInfoSetProperty( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
250     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
251
252     r = MsiSummaryInfoSetProperty( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
253     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
254
255     r = MsiSummaryInfoSetProperty( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
256     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
257
258     r = MsiSummaryInfoSetProperty( hsi, 9, VT_LPSTR, 0, NULL, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
259     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
260
261     r = MsiSummaryInfoSetProperty( hsi, 14, VT_I4, 100, NULL, NULL );
262     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
263
264     r = MsiSummaryInfoSetProperty( hsi, 15, VT_I4, 0, NULL, NULL );
265     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
266
267     r = MsiSummaryInfoPersist( hsi );
268     ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
269
270     r = MsiCloseHandle( hsi );
271     ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
272
273     r = MsiCloseHandle( hdb );
274     ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
275 }
276
277 static void create_database( const char *filename, const struct msi_table *tables, UINT num_tables )
278 {
279     MSIHANDLE hdb;
280     UINT r, i;
281
282     r = MsiOpenDatabaseA( filename, MSIDBOPEN_CREATE, &hdb );
283     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
284
285     /* import the tables into the database */
286     for (i = 0; i < num_tables; i++)
287     {
288         const struct msi_table *table = &tables[i];
289
290         write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
291
292         r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
293         ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
294
295         DeleteFileA( table->filename );
296     }
297
298     r = MsiDatabaseCommit( hdb );
299     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
300
301     MsiCloseHandle( hdb );
302     set_suminfo( filename );
303 }
304
305 /* data for generating a patch */
306
307 /* table names - encoded as in an msi database file */
308 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
309 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
310 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
311 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
312 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
313 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
314 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
315                                  0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
316                                  0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
317 /* substorage names */
318 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
319                                  0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
320 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
321                                  0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
322
323 /* data in each table */
324 static const WCHAR p_data0[] = { /* _Columns */
325     0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
326     0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
327 };
328 static const WCHAR p_data1[] = { /* _Tables */
329     0x0001
330 };
331 static const char p_data2[] = { /* _StringData */
332     "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
333 };
334 static const WCHAR p_data3[] = { /* _StringPool */
335 /* len, refs */
336      0,  0,     /* string 0 '' */
337     16,  5,     /* string 1 'MsiPatchSequence' */
338     11,  1,     /* string 2 'PatchFamily' */
339     11,  1,     /* string 3 'ProductCode' */
340      8,  1,     /* string 4 'Sequence' */
341     10,  1,     /* string 5 'Attributes' */
342     15,  1,     /* string 6 '1.1.19388.37230' */
343     32,  1,     /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
344 };
345 static const char p_data4[] = { /* CAB_msitest */
346     0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
347     0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
348     0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
349     0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
350     0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
351     0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
352     0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
353     0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
354     0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
355     0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
356     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
357     0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
358     0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
359     0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
360 };
361 static const WCHAR p_data5[] = { /* MsiPatchSequence */
362     0x0007, 0x0000, 0x0006, 0x8000
363 };
364 static const char p_data6[] = { /* SummaryInformation */
365     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
366     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
368     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
369     0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
370     0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
371     0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
372     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
373     0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
374     0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
375     0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
376     0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
377     0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
378     0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
379     0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
380     0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
381     0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
382     0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
383     0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
384     0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
385     0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
386     0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
387     0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
388     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
389     0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
390 };
391
392 struct table_data {
393     LPCWSTR name;
394     const void *data;
395     DWORD size;
396 };
397
398 static const struct table_data table_patch_data[] = {
399     { p_name0, p_data0, sizeof p_data0 },
400     { p_name1, p_data1, sizeof p_data1 },
401     { p_name2, p_data2, sizeof p_data2 - 1 },
402     { p_name3, p_data3, sizeof p_data3 },
403     { p_name4, p_data4, sizeof p_data4 },
404     { p_name5, p_data5, sizeof p_data5 },
405     { p_name6, p_data6, sizeof p_data6 }
406 };
407
408 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
409
410 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
411 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
412 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
413 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
414                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
415                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
416
417 static const WCHAR t1_data0[] = { /* File */
418     0x0008, 0x0001, 0x03ea, 0x8000
419 };
420 static const char t1_data1[] = { /* _StringData */
421     "patch.txt"
422 };
423 static const WCHAR t1_data2[] = { /* _StringPool */
424 /* len, refs */
425      0,  0,     /* string 0 '' */
426      9,  1,     /* string 1 'patch.txt' */
427 };
428 static const char t1_data3[] = { /* SummaryInformation */
429     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
430     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
432     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
433     0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
434     0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
435     0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
436     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
437     0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
438     0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
439     0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
440     0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
441     0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
442     0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
443     0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
444     0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
445     0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
446     0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
447     0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
448     0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
449     0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
450     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
451     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
452     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
453     0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
454     0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
455     0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
456     0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
457     0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
458     0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
459     0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
460     0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
461     0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
462     0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
463     0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
464     0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
465     0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
466     0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
467     0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
468     0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
469     0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
470     0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
471 };
472
473 static const struct table_data table_transform1_data[] = {
474     { t1_name0, t1_data0, sizeof t1_data0 },
475     { t1_name1, t1_data1, sizeof t1_data1 - 1 },
476     { t1_name2, t1_data2, sizeof t1_data2 },
477     { t1_name3, t1_data3, sizeof t1_data3 }
478 };
479
480 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
481
482 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
483 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
484 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
485 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
486 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
487 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
488 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
489                                   0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
490 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
491 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
492 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
493                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
494                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
495
496 static const WCHAR t2_data0[] = { /* File */
497     0x00c0, 0x0001, 0x9000, 0x83e8
498 };
499 static const WCHAR t2_data1[] = { /* Media */
500     0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
501 };
502 static const WCHAR t2_data2[] = { /* _Columns */
503     0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
504     0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
505     0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
506     0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
507     0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
508     0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
509     0x000e, 0x8900
510 };
511 static const WCHAR t2_data3[] = { /* _Tables */
512     0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
513 };
514 static const WCHAR t2_data4[] = { /* Property */
515     0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
516 };
517 static const WCHAR t2_data5[] = { /* PatchPackage */
518     0x0201, 0x0013, 0x8002
519 };
520 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
521     0x0301, 0x0006, 0x0000, 0x87d1
522 };
523 static const char t2_data7[] = { /* _StringData */
524     "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
525     "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
526     "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
527 };
528 static const WCHAR t2_data8[] = { /* _StringPool */
529 /* len, refs */
530      0,  0,     /* string 0 '' */
531      9,  1,     /* string 1 'patch.txt' */
532     22,  1,     /* string 2 'PATCHNEWSUMMARYSUBJECT' */
533     21,  1,     /* string 3 'Installation Database' */
534     19,  1,     /* string 4 'PATCHNEWPACKAGECODE' */
535     38,  1,     /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
536     10,  1,     /* string 6 'PatchFiles' */
537     12,  1,     /* string 7 '#CAB_msitest' */
538      4,  1,     /* string 8 'prop' */
539      5,  7,     /* string 9 'Patch' */
540      5,  1,     /* string 10 'File_' */
541      8,  1,     /* string 11 'Sequence' */
542      9,  1,     /* string 12 'PatchSize' */
543     10,  1,     /* string 13 'Attributes' */
544      6,  2,     /* string 14 'Header' */
545     10,  1,     /* string 15 'StreamRef_' */
546     12,  3,     /* string 16 'PatchPackage' */
547      7,  1,     /* string 17 'PatchId' */
548      6,  1,     /* string 18 'Media_' */
549     38,  1,     /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
550     15,  3,     /* string 20 'MsiPatchHeaders' */
551      9,  1      /* string 21 'StreamRef' */
552 };
553 static const char t2_data9[] = { /* SummaryInformation */
554     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
555     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
556     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
557     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
558     0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
559     0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
560     0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
561     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
562     0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
563     0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
564     0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
565     0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
566     0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
567     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
568     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
569     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
570     0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
571     0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
572     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
573     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
574     0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
575     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
576     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
577     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
578     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
579     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
580     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
581     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
582     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
583     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
584     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
585     0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
586     0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
587     0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
588     0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
589     0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
590 };
591
592 static const struct table_data table_transform2_data[] = {
593     { t2_name0, t2_data0, sizeof t2_data0 },
594     { t2_name1, t2_data1, sizeof t2_data1 },
595     { t2_name2, t2_data2, sizeof t2_data2 },
596     { t2_name3, t2_data3, sizeof t2_data3 },
597     { t2_name4, t2_data4, sizeof t2_data4 },
598     { t2_name5, t2_data5, sizeof t2_data5 },
599     { t2_name6, t2_data6, sizeof t2_data6 },
600     { t2_name7, t2_data7, sizeof t2_data7 - 1 },
601     { t2_name8, t2_data8, sizeof t2_data8 },
602     { t2_name9, t2_data9, sizeof t2_data9 }
603 };
604
605 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
606
607 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
608 {
609     IStream *stm;
610     DWORD i, count;
611     HRESULT r;
612
613     for (i = 0; i < num_tables; i++)
614     {
615         r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
616         if (FAILED( r ))
617         {
618             ok( 0, "failed to create stream 0x%08x\n", r );
619             continue;
620         }
621
622         r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
623         if (FAILED( r ) || count != tables[i].size)
624             ok( 0, "failed to write stream\n" );
625         IStream_Release( stm );
626     }
627 }
628
629 static void create_patch( const char *filename )
630 {
631     IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
632     WCHAR *filenameW;
633     HRESULT r;
634     int len;
635     const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
636
637     const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
638     const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
639
640     len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
641     filenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
642     MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
643
644     r = StgCreateDocfile( filenameW, mode, 0, &stg );
645     HeapFree( GetProcessHeap(), 0, filenameW );
646     ok( r == S_OK, "failed to create storage 0x%08x\n", r );
647     if (!stg)
648         return;
649
650     r = IStorage_SetClass( stg, &CLSID_MsiPatch );
651     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
652
653     write_tables( stg, table_patch_data, NUM_PATCH_TABLES );
654
655     r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
656     ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
657
658     r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
659     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
660
661     write_tables( stg1, table_transform1_data, NUM_TRANSFORM1_TABLES );
662     IStorage_Release( stg1 );
663
664     r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
665     ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
666
667     r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
668     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
669
670     write_tables( stg2, table_transform2_data, NUM_TRANSFORM2_TABLES );
671     IStorage_Release( stg2 );
672     IStorage_Release( stg );
673 }
674
675 static void test_simple_patch( void )
676 {
677     UINT r;
678     DWORD size;
679     char path[MAX_PATH];
680     const char *query;
681     MSIHANDLE hpackage, hdb, hview, hrec;
682
683     if (!pMsiApplyPatchA)
684     {
685         win_skip("MsiApplyPatchA is not available\n");
686         return;
687     }
688
689     CreateDirectoryA( "msitest", NULL );
690     create_file( "msitest\\patch.txt", 1000 );
691
692     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
693     create_patch( mspfile );
694
695     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
696
697     r = MsiInstallProductA( msifile, NULL );
698     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
699
700     size = get_pf_file_size( "msitest\\patch.txt" );
701     ok( size == 1000, "expected 1000, got %u\n", size );
702
703     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
704     ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
705         "expected ERROR_SUCCESS, got %u\n", r );
706
707     if (r == ERROR_PATCH_PACKAGE_INVALID)
708     {
709         win_skip("Windows Installer < 3.0 detected\n");
710         return;
711     }
712
713     size = get_pf_file_size( "msitest\\patch.txt" );
714     ok( size == 1002, "expected 1002, got %u\n", size );
715
716     strcpy( path, CURR_DIR );
717     strcat( path, "\\" );
718     strcat( path, msifile );
719
720     /* show that MsiOpenPackage applies registered patches */
721     r = MsiOpenPackageA( path, &hpackage );
722     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
723
724     hdb = MsiGetActiveDatabase( hpackage );
725     ok( hdb, "failed to get database handle\n" );
726
727     query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
728     r = MsiDatabaseOpenView( hdb, query, &hview );
729     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
730
731     r = MsiViewExecute( hview, 0 );
732     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
733
734     r = MsiViewFetch( hview, &hrec );
735     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
736
737     MsiCloseHandle( hrec );
738     MsiViewClose( hview );
739     MsiCloseHandle( hview );
740     MsiCloseHandle( hdb );
741     MsiCloseHandle( hpackage );
742
743     /* show that patches are not committed to the local package database */
744     size = sizeof(path);
745     r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
746                             "LocalPackage", path, &size );
747     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
748
749     r = MsiOpenDatabaseA( path, MSIDBOPEN_READONLY, &hdb );
750     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
751
752     r = MsiDatabaseOpenView( hdb, query, &hview );
753     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
754
755     r = MsiViewExecute( hview, 0 );
756     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
757
758     r = MsiViewFetch( hview, &hrec );
759     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
760
761     MsiCloseHandle( hrec );
762     MsiViewClose( hview );
763     MsiCloseHandle( hview );
764     MsiCloseHandle( hdb );
765
766     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
767     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
768
769     ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
770     ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
771
772     DeleteFileA( msifile );
773     DeleteFileA( mspfile );
774     RemoveDirectoryA( "msitest" );
775 }
776
777 static void test_MsiOpenDatabase( void )
778 {
779     UINT r;
780     MSIHANDLE hdb;
781
782     r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE, &hdb );
783     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
784
785     r = MsiDatabaseCommit( hdb );
786     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
787     MsiCloseHandle( hdb );
788
789     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
790     ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
791     DeleteFileA( mspfile );
792
793     r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
794     ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
795
796     r = MsiDatabaseCommit( hdb );
797     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
798     MsiCloseHandle( hdb );
799
800     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
801     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
802     MsiCloseHandle( hdb );
803     DeleteFileA( mspfile );
804
805     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
806     create_patch( mspfile );
807
808     r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
809     ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
810
811     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
812     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
813     MsiCloseHandle( hdb );
814
815     DeleteFileA( msifile );
816     DeleteFileA( mspfile );
817 }
818
819 static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry )
820 {
821     static char fmt[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
822     char query[0x100];
823     UINT r;
824     MSIHANDLE hview, hrec;
825
826     sprintf( query, fmt, table, entry );
827     r = MsiDatabaseOpenView( hdb, query, &hview );
828     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
829
830     r = MsiViewExecute( hview, 0 );
831     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
832
833     r = MsiViewFetch( hview, &hrec );
834     MsiViewClose( hview );
835     MsiCloseHandle( hview );
836     MsiCloseHandle( hrec );
837     return r;
838 }
839
840 static void test_system_tables( void )
841 {
842     UINT r;
843     const char *query;
844     MSIHANDLE hproduct, hdb, hview, hrec;
845
846     if (!pMsiApplyPatchA)
847     {
848         win_skip("MsiApplyPatchA is not available\n");
849         return;
850     }
851
852     CreateDirectoryA( "msitest", NULL );
853     create_file( "msitest\\patch.txt", 1000 );
854
855     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
856     create_patch( mspfile );
857
858     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
859
860     r = MsiInstallProductA( msifile, NULL );
861     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
862
863     r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
864     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
865
866     hdb = MsiGetActiveDatabase( hproduct );
867     ok( hdb, "failed to get database handle\n" );
868
869     r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
870     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
871
872     query = "SELECT * FROM `_Storages`";
873     r = MsiDatabaseOpenView( hdb, query, &hview );
874     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
875
876     r = MsiViewExecute( hview, 0 );
877     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
878
879     r = MsiViewFetch( hview, &hrec );
880     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
881
882     r = find_entry( hdb, "_Tables", "Directory" );
883     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
884
885     r = find_entry( hdb, "_Tables", "File" );
886     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
887
888     r = find_entry( hdb, "_Tables", "Component" );
889     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
890
891     r = find_entry( hdb, "_Tables", "Feature" );
892     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
893
894     r = find_entry( hdb, "_Tables", "FeatureComponents" );
895     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
896
897     r = find_entry( hdb, "_Tables", "Property" );
898     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
899
900     r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
901     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
902
903     r = find_entry( hdb, "_Tables", "Media" );
904     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
905
906     r = find_entry( hdb, "_Tables", "_Property" );
907     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
908
909     MsiCloseHandle( hdb );
910     MsiCloseHandle( hproduct );
911
912     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
913     ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
914         "expected ERROR_SUCCESS, got %u\n", r );
915
916     if (r == ERROR_PATCH_PACKAGE_INVALID)
917     {
918         win_skip("Windows Installer < 3.0 detected\n");
919         return;
920     }
921
922     r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
923     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
924
925     hdb = MsiGetActiveDatabase( hproduct );
926     ok( hdb, "failed to get database handle\n" );
927
928     r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
929     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
930
931     query = "SELECT * FROM `_Storages`";
932     r = MsiDatabaseOpenView( hdb, query, &hview );
933     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
934
935     r = MsiViewExecute( hview, 0 );
936     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
937
938     r = MsiViewFetch( hview, &hrec );
939     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
940
941     r = find_entry( hdb, "_Tables", "Directory" );
942     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
943
944     r = find_entry( hdb, "_Tables", "File" );
945     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
946
947     r = find_entry( hdb, "_Tables", "Component" );
948     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
949
950     r = find_entry( hdb, "_Tables", "Feature" );
951     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
952
953     r = find_entry( hdb, "_Tables", "FeatureComponents" );
954     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
955
956     r = find_entry( hdb, "_Tables", "Property" );
957     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
958
959     r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
960     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
961
962     r = find_entry( hdb, "_Tables", "Media" );
963     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
964
965     r = find_entry( hdb, "_Tables", "_Property" );
966     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
967
968     r = find_entry( hdb, "_Tables", "MsiPatchHeaders" );
969     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
970
971     r = find_entry( hdb, "_Tables", "Patch" );
972     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
973
974     r = find_entry( hdb, "_Tables", "PatchPackage" );
975     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
976
977     MsiCloseHandle( hdb );
978     MsiCloseHandle( hproduct );
979
980     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
981     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
982
983     DeleteFileA( msifile );
984     DeleteFileA( mspfile );
985     RemoveDirectoryA( "msitest" );
986 }
987
988 static void test_patch_registration( void )
989 {
990     UINT r, size;
991     char buffer[MAX_PATH];
992
993     if (!pMsiApplyPatchA || !pMsiGetPatchInfoExA)
994     {
995         win_skip("required functions not available\n");
996         return;
997     }
998
999     CreateDirectoryA( "msitest", NULL );
1000     create_file( "msitest\\patch.txt", 1000 );
1001
1002     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
1003     create_patch( mspfile );
1004
1005     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1006
1007     r = MsiInstallProductA( msifile, NULL );
1008     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1009
1010     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1011     ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
1012         "expected ERROR_SUCCESS, got %u\n", r );
1013
1014     if (r == ERROR_PATCH_PACKAGE_INVALID)
1015     {
1016         win_skip("Windows Installer < 3.0 detected\n");
1017         return;
1018     }
1019
1020     buffer[0] = 0;
1021     size = sizeof(buffer);
1022     r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1023                              "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1024                               NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1025                               INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1026     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1027     ok( buffer[0], "buffer empty\n" );
1028
1029     buffer[0] = 0;
1030     size = sizeof(buffer);
1031     r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1032                              "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1033                              NULL, MSIINSTALLCONTEXT_MACHINE,
1034                              INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1035     ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1036
1037     buffer[0] = 0;
1038     size = sizeof(buffer);
1039     r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1040                              "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1041                              NULL, MSIINSTALLCONTEXT_USERMANAGED,
1042                              INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1043     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1044     ok( !buffer[0], "got %s\n", buffer );
1045
1046     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1047     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1048
1049     buffer[0] = 0;
1050     size = sizeof(buffer);
1051     r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1052                              "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1053                               NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1054                               INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1055     ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1056
1057     DeleteFileA( msifile );
1058     DeleteFileA( mspfile );
1059     RemoveDirectoryA( "msitest" );
1060 }
1061
1062 START_TEST(patch)
1063 {
1064     DWORD len;
1065     char temp_path[MAX_PATH], prev_path[MAX_PATH];
1066
1067     init_function_pointers();
1068
1069     GetCurrentDirectoryA( MAX_PATH, prev_path );
1070     GetTempPath( MAX_PATH, temp_path );
1071     SetCurrentDirectoryA( temp_path );
1072
1073     strcpy( CURR_DIR, temp_path );
1074     len = strlen( CURR_DIR );
1075
1076     if (len && (CURR_DIR[len - 1] == '\\'))
1077         CURR_DIR[len - 1] = 0;
1078
1079     get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
1080
1081     test_simple_patch();
1082     test_MsiOpenDatabase();
1083     test_system_tables();
1084     test_patch_registration();
1085
1086     SetCurrentDirectoryA( prev_path );
1087 }