janitorial: Remove remaining NULL checks before free() (found by Smatch).
[wine] / dlls / msi / package.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004 Aric Stewart for CodeWeavers
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 NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "shlwapi.h"
31 #include "wingdi.h"
32 #include "wine/debug.h"
33 #include "msi.h"
34 #include "msiquery.h"
35 #include "objidl.h"
36 #include "wincrypt.h"
37 #include "winuser.h"
38 #include "wininet.h"
39 #include "urlmon.h"
40 #include "shlobj.h"
41 #include "wine/unicode.h"
42 #include "objbase.h"
43 #include "msidefs.h"
44
45 #include "msipriv.h"
46 #include "action.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49
50 static void msi_free_properties( MSIPACKAGE *package );
51
52 static void MSI_FreePackage( MSIOBJECTHDR *arg)
53 {
54     MSIPACKAGE *package= (MSIPACKAGE*) arg;
55
56     if( package->dialog )
57         msi_dialog_destroy( package->dialog );
58     ACTION_free_package_structures(package);
59
60     msi_free_properties( package );
61
62     msiobj_release( &package->db->hdr );
63 }
64
65 static UINT clone_properties( MSIPACKAGE *package )
66 {
67     MSIQUERY * view = NULL;
68     UINT rc;
69     static const WCHAR Query[] = {
70        'S','E','L','E','C','T',' ','*',' ',
71        'F','R','O','M',' ','`','P','r','o','p','e','r','t','y','`',0};
72
73     /* clone the existing properties */
74     rc = MSI_DatabaseOpenViewW( package->db, Query, &view );
75     if (rc != ERROR_SUCCESS)
76         return rc;
77
78     rc = MSI_ViewExecute(view, 0);
79     if (rc != ERROR_SUCCESS)
80     {
81         MSI_ViewClose(view);
82         msiobj_release(&view->hdr);
83         return rc;
84     }
85     while (1)
86     {
87         MSIRECORD * row;
88         LPCWSTR name, value;
89
90         rc = MSI_ViewFetch(view,&row);
91         if (rc != ERROR_SUCCESS)
92             break;
93
94         name = MSI_RecordGetString( row, 1 );
95         value = MSI_RecordGetString( row, 2 );
96         MSI_SetPropertyW( package, name, value );
97
98         msiobj_release( &row->hdr );
99     }
100     MSI_ViewClose(view);
101     msiobj_release(&view->hdr);
102
103     return rc;
104 }
105
106 /*
107  * set_installed_prop
108  *
109  * Sets the "Installed" property to indicate that
110  *  the product is installed for the current user.
111  */
112 static UINT set_installed_prop( MSIPACKAGE *package )
113 {
114     static const WCHAR szInstalled[] = {
115         'I','n','s','t','a','l','l','e','d',0 };
116     WCHAR val[2] = { '1', 0 };
117     HKEY hkey = 0;
118     UINT r;
119
120     r = MSIREG_OpenUninstallKey( package->ProductCode, &hkey, FALSE );
121     if (r == ERROR_SUCCESS)
122     {
123         RegCloseKey( hkey );
124         MSI_SetPropertyW( package, szInstalled, val );
125     }
126
127     return r;
128 }
129
130 /*
131  * There are a whole slew of these we need to set
132  *
133  *
134 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
135  */
136 static VOID set_installer_properties(MSIPACKAGE *package)
137 {
138     WCHAR pth[MAX_PATH];
139     WCHAR *ptr;
140     OSVERSIONINFOA OSVersion;
141     MEMORYSTATUSEX msex;
142     DWORD verval;
143     WCHAR verstr[10], bufstr[20];
144     HDC dc;
145
146     static const WCHAR cszbs[]={'\\',0};
147     static const WCHAR CFF[] = 
148 {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
149     static const WCHAR PFF[] = 
150 {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
151     static const WCHAR CADF[] = 
152 {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
153     static const WCHAR FaF[] = 
154 {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
155     static const WCHAR FoF[] = 
156 {'F','o','n','t','s','F','o','l','d','e','r',0};
157     static const WCHAR SendTF[] = 
158 {'S','e','n','d','T','o','F','o','l','d','e','r',0};
159     static const WCHAR SMF[] = 
160 {'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0};
161     static const WCHAR StF[] = 
162 {'S','t','a','r','t','u','p','F','o','l','d','e','r',0};
163     static const WCHAR TemplF[] = 
164 {'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0};
165     static const WCHAR DF[] = 
166 {'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
167     static const WCHAR PMF[] = 
168 {'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0};
169     static const WCHAR ATF[] = 
170 {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
171     static const WCHAR ADF[] = 
172 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
173     static const WCHAR SF[] = 
174 {'S','y','s','t','e','m','F','o','l','d','e','r',0};
175     static const WCHAR SF16[] = 
176 {'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0};
177     static const WCHAR LADF[] = 
178 {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
179     static const WCHAR MPF[] = 
180 {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
181     static const WCHAR PF[] = 
182 {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
183     static const WCHAR WF[] = 
184 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
185     static const WCHAR WV[] = 
186 {'W','i','n','d','o','w','s','V','o','l','u','m','e',0};
187     static const WCHAR TF[]=
188 {'T','e','m','p','F','o','l','d','e','r',0};
189     static const WCHAR szAdminUser[] =
190 {'A','d','m','i','n','U','s','e','r',0};
191     static const WCHAR szPriv[] =
192 {'P','r','i','v','i','l','e','g','e','d',0};
193     static const WCHAR szOne[] =
194 {'1',0};
195     static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 };
196     static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 };
197     static const WCHAR szFormat[] = {'%','l','i',0};
198     static const WCHAR szWinBuild[] =
199 {'W','i','n','d','o','w','s','B','u','i','l','d', 0 };
200     static const WCHAR szSPL[] = 
201 {'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 };
202     static const WCHAR szSix[] = {'6',0 };
203
204     static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 };
205     static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 };
206     static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0};
207 /* Screen properties */
208     static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
209     static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
210     static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0};
211     static const WCHAR szScreenFormat[] = {'%','d',0};
212     static const WCHAR szIntel[] = { 'I','n','t','e','l',0 };
213     static const WCHAR szAllUsers[] = { 'A','L','L','U','S','E','R','S',0 };
214     SYSTEM_INFO sys_info;
215
216     /*
217      * Other things that probably should be set:
218      *
219      * SystemLanguageID ComputerName UserLanguageID LogonUser VirtualMemory
220      * Intel ShellAdvSupport DefaultUIFont VersionDatabase PackagecodeChanging
221      * ProductState CaptionHeight BorderTop BorderSide TextHeight
222      * RedirectedDllSupport Time Date Privileged
223      */
224
225     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
226     strcatW(pth,cszbs);
227     MSI_SetPropertyW(package, CFF, pth);
228
229     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
230     strcatW(pth,cszbs);
231     MSI_SetPropertyW(package, PFF, pth);
232
233     SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
234     strcatW(pth,cszbs);
235     MSI_SetPropertyW(package, CADF, pth);
236
237     SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth);
238     strcatW(pth,cszbs);
239     MSI_SetPropertyW(package, FaF, pth);
240
241     SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth);
242     strcatW(pth,cszbs);
243     MSI_SetPropertyW(package, FoF, pth);
244
245     SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth);
246     strcatW(pth,cszbs);
247     MSI_SetPropertyW(package, SendTF, pth);
248
249     SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth);
250     strcatW(pth,cszbs);
251     MSI_SetPropertyW(package, SMF, pth);
252
253     SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth);
254     strcatW(pth,cszbs);
255     MSI_SetPropertyW(package, StF, pth);
256
257     SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth);
258     strcatW(pth,cszbs);
259     MSI_SetPropertyW(package, TemplF, pth);
260
261     SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth);
262     strcatW(pth,cszbs);
263     MSI_SetPropertyW(package, DF, pth);
264
265     SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth);
266     strcatW(pth,cszbs);
267     MSI_SetPropertyW(package, PMF, pth);
268
269     SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
270     strcatW(pth,cszbs);
271     MSI_SetPropertyW(package, ATF, pth);
272
273     SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
274     strcatW(pth,cszbs);
275     MSI_SetPropertyW(package, ADF, pth);
276
277     SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
278     strcatW(pth,cszbs);
279     MSI_SetPropertyW(package, SF, pth);
280     MSI_SetPropertyW(package, SF16, pth);
281
282     SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
283     strcatW(pth,cszbs);
284     MSI_SetPropertyW(package, LADF, pth);
285
286     SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
287     strcatW(pth,cszbs);
288     MSI_SetPropertyW(package, MPF, pth);
289
290     SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
291     strcatW(pth,cszbs);
292     MSI_SetPropertyW(package, PF, pth);
293
294     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
295     strcatW(pth,cszbs);
296     MSI_SetPropertyW(package, WF, pth);
297     
298     /* Physical Memory is specified in MB. Using total amount. */
299     msex.dwLength = sizeof(msex);
300     GlobalMemoryStatusEx( &msex );
301     sprintfW( bufstr, szScreenFormat, (int)(msex.ullTotalPhys/1024/1024));
302     MSI_SetPropertyW(package, szPhysicalMemory, bufstr);
303
304     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
305     ptr = strchrW(pth,'\\');
306     if (ptr)
307         *(ptr+1) = 0;
308     MSI_SetPropertyW(package, WV, pth);
309     
310     GetTempPathW(MAX_PATH,pth);
311     MSI_SetPropertyW(package, TF, pth);
312
313
314     /* in a wine environment the user is always admin and privileged */
315     MSI_SetPropertyW(package,szAdminUser,szOne);
316     MSI_SetPropertyW(package,szPriv,szOne);
317     MSI_SetPropertyW(package, szAllUsers, szOne);
318
319     /* set the os things */
320     OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
321     GetVersionExA(&OSVersion);
322     verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
323     sprintfW(verstr,szFormat,verval);
324     switch (OSVersion.dwPlatformId)
325     {
326         case VER_PLATFORM_WIN32_WINDOWS:    
327             MSI_SetPropertyW(package,v9x,verstr);
328             break;
329         case VER_PLATFORM_WIN32_NT:
330             MSI_SetPropertyW(package,vNT,verstr);
331             break;
332     }
333     sprintfW(verstr,szFormat,OSVersion.dwBuildNumber);
334     MSI_SetPropertyW(package,szWinBuild,verstr);
335     /* just fudge this */
336     MSI_SetPropertyW(package,szSPL,szSix);
337
338     sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION);
339     MSI_SetPropertyW( package, szVersionMsi, bufstr );
340
341     GetSystemInfo( &sys_info );
342     if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
343     {
344         sprintfW( bufstr, szScreenFormat, sys_info.wProcessorLevel );
345         MSI_SetPropertyW( package, szIntel, bufstr );
346     }
347
348     /* Screen properties. */
349     dc = GetDC(0);
350     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, HORZRES ) );
351     MSI_SetPropertyW( package, szScreenX, bufstr );
352     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, VERTRES ));
353     MSI_SetPropertyW( package, szScreenY, bufstr );
354     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, BITSPIXEL ));
355     MSI_SetPropertyW( package, szColorBits, bufstr );
356     ReleaseDC(0, dc);
357 }
358
359 static UINT msi_get_word_count( MSIPACKAGE *package )
360 {
361     UINT rc;
362     INT word_count;
363     MSIHANDLE suminfo;
364     MSIHANDLE hdb = alloc_msihandle( &package->db->hdr );
365
366     if (!hdb) {
367         ERR("Unable to allocate handle\n");
368         return 0;
369     }
370     rc = MsiGetSummaryInformationW( hdb, NULL, 0, &suminfo );
371     MsiCloseHandle(hdb);
372     if (rc != ERROR_SUCCESS)
373     {
374         ERR("Unable to open Summary Information\n");
375         return 0;
376     }
377
378     rc = MsiSummaryInfoGetPropertyW( suminfo, PID_WORDCOUNT, NULL,
379                                      &word_count, NULL, NULL, NULL );
380     if (rc != ERROR_SUCCESS)
381     {
382         ERR("Unable to query word count\n");
383         MsiCloseHandle(suminfo);
384         return 0;
385     }
386
387     MsiCloseHandle(suminfo);
388     return word_count;
389 }
390
391 MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db )
392 {
393     static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 };
394     static const WCHAR szpi[] = {'%','i',0};
395     static const WCHAR szProductCode[] = {
396         'P','r','o','d','u','c','t','C','o','d','e',0};
397     MSIPACKAGE *package = NULL;
398     WCHAR uilevel[10];
399     int i;
400
401     TRACE("%p\n", db);
402
403     package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
404                                MSI_FreePackage );
405     if( package )
406     {
407         msiobj_addref( &db->hdr );
408
409         package->db = db;
410         list_init( &package->components );
411         list_init( &package->features );
412         list_init( &package->files );
413         list_init( &package->tempfiles );
414         list_init( &package->folders );
415         package->ActionFormat = NULL;
416         package->LastAction = NULL;
417         package->dialog = NULL;
418         package->next_dialog = NULL;
419         list_init( &package->subscriptions );
420         list_init( &package->appids );
421         list_init( &package->classes );
422         list_init( &package->mimes );
423         list_init( &package->extensions );
424         list_init( &package->progids );
425         list_init( &package->RunningActions );
426
427         package->WordCount = msi_get_word_count( package );
428
429         /* OK, here is where we do a slew of things to the database to 
430          * prep for all that is to come as a package */
431
432         for (i=0; i<PROPERTY_HASH_SIZE; i++)
433             list_init( &package->props[i] );
434
435         clone_properties( package );
436         set_installer_properties(package);
437         sprintfW(uilevel,szpi,gUILevel);
438         MSI_SetPropertyW(package, szLevel, uilevel);
439
440         package->ProductCode = msi_dup_property( package, szProductCode );
441         set_installed_prop( package );
442     }
443
444     return package;
445 }
446
447 /*
448  * copy_package_to_temp   [internal]
449  *
450  * copy the msi file to a temp file to prevent locking a CD
451  * with a multi disc install 
452  *
453  * FIXME: I think this is wrong, and instead of copying the package,
454  *        we should read all the tables to memory, then open the
455  *        database to read binary streams on demand.
456  */ 
457 static LPCWSTR copy_package_to_temp( LPCWSTR szPackage, LPWSTR filename )
458 {
459     WCHAR path[MAX_PATH];
460     static const WCHAR szMSI[] = {'M','S','I',0};
461
462     GetTempPathW( MAX_PATH, path );
463     GetTempFileNameW( path, szMSI, 0, filename );
464
465     if( !CopyFileW( szPackage, filename, FALSE ) )
466     {
467         ERR("failed to copy package %s\n", debugstr_w(szPackage) );
468         return szPackage;
469     }
470
471     TRACE("Opening relocated package %s\n", debugstr_w( filename ));
472     return filename;
473 }
474
475 LPCWSTR msi_download_file( LPCWSTR szUrl, LPWSTR filename )
476 {
477     LPINTERNET_CACHE_ENTRY_INFOW cache_entry;
478     DWORD size = 0;
479     HRESULT hr;
480
481     /* call will always fail, becase size is 0,
482      * but will return ERROR_FILE_NOT_FOUND first
483      * if the file doesn't exist
484      */
485     GetUrlCacheEntryInfoW( szUrl, NULL, &size );
486     if ( GetLastError() != ERROR_FILE_NOT_FOUND )
487     {
488         cache_entry = HeapAlloc( GetProcessHeap(), 0, size );
489         if ( !GetUrlCacheEntryInfoW( szUrl, cache_entry, &size ) )
490         {
491             HeapFree( GetProcessHeap(), 0, cache_entry );
492             return szUrl;
493         }
494
495         lstrcpyW( filename, cache_entry->lpszLocalFileName );
496         HeapFree( GetProcessHeap(), 0, cache_entry );
497         return filename;
498     }
499
500     hr = URLDownloadToCacheFileW( NULL, szUrl, filename, MAX_PATH, 0, NULL );
501     if ( FAILED(hr) )
502         return szUrl;
503
504     return filename;
505 }
506
507 UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage)
508 {
509     MSIDATABASE *db = NULL;
510     MSIPACKAGE *package;
511     MSIHANDLE handle;
512     UINT r;
513
514     static const WCHAR OriginalDatabase[] =
515         {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
516     static const WCHAR Database[] = {'D','A','T','A','B','A','S','E',0};
517
518     TRACE("%s %p\n", debugstr_w(szPackage), pPackage);
519
520     if( szPackage[0] == '#' )
521     {
522         handle = atoiW(&szPackage[1]);
523         db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
524         if( !db )
525             return ERROR_INVALID_HANDLE;
526     }
527     else
528     {
529         WCHAR temppath[MAX_PATH];
530         LPCWSTR file;
531
532         if ( UrlIsW( szPackage, URLIS_URL ) )
533             file = msi_download_file( szPackage, temppath );
534         else
535             file = copy_package_to_temp( szPackage, temppath );
536
537         r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &db );
538
539         if (file != szPackage)
540             DeleteFileW( file );
541
542         if( r != ERROR_SUCCESS )
543         {
544             if (GetLastError() == ERROR_FILE_NOT_FOUND)
545                 msi_ui_error( 4, MB_OK | MB_ICONWARNING );
546
547             return r;
548         }
549     }
550
551     package = MSI_CreatePackage( db );
552     msiobj_release( &db->hdr );
553     if( !package )
554         return ERROR_FUNCTION_FAILED;
555
556     if( szPackage[0] != '#' )
557     {
558         MSI_SetPropertyW( package, OriginalDatabase, szPackage );
559         MSI_SetPropertyW( package, Database, szPackage );
560     }
561     else
562     {
563         MSI_SetPropertyW( package, OriginalDatabase, db->path );
564         MSI_SetPropertyW( package, Database, db->path );
565     }
566
567     *pPackage = package;
568
569     return ERROR_SUCCESS;
570 }
571
572 UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
573 {
574     MSIPACKAGE *package = NULL;
575     UINT ret;
576
577     TRACE("%s %08x %p\n", debugstr_w(szPackage), dwOptions, phPackage );
578
579     if( szPackage == NULL )
580         return ERROR_INVALID_PARAMETER;
581
582     if( dwOptions )
583         FIXME("dwOptions %08x not supported\n", dwOptions);
584
585     ret = MSI_OpenPackageW( szPackage, &package );
586     if( ret == ERROR_SUCCESS )
587     {
588         *phPackage = alloc_msihandle( &package->hdr );
589         if (! *phPackage)
590             ret = ERROR_NOT_ENOUGH_MEMORY;
591         msiobj_release( &package->hdr );
592     }
593
594     return ret;
595 }
596
597 UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
598 {
599     return MsiOpenPackageExW( szPackage, 0, phPackage );
600 }
601
602 UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
603 {
604     LPWSTR szwPack = NULL;
605     UINT ret;
606
607     if( szPackage )
608     {
609         szwPack = strdupAtoW( szPackage );
610         if( !szwPack )
611             return ERROR_OUTOFMEMORY;
612     }
613
614     ret = MsiOpenPackageExW( szwPack, dwOptions, phPackage );
615
616     msi_free( szwPack );
617
618     return ret;
619 }
620
621 UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
622 {
623     return MsiOpenPackageExA( szPackage, 0, phPackage );
624 }
625
626 MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
627 {
628     MSIPACKAGE *package;
629     MSIHANDLE handle = 0;
630
631     TRACE("(%ld)\n",hInstall);
632
633     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
634     if( package)
635     {
636         handle = alloc_msihandle( &package->db->hdr );
637         msiobj_release( &package->hdr );
638     }
639
640     return handle;
641 }
642
643 INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType,
644                                MSIRECORD *record)
645 {
646     static const WCHAR szActionData[] =
647         {'A','c','t','i','o','n','D','a','t','a',0};
648     static const WCHAR szSetProgress[] =
649         {'S','e','t','P','r','o','g','r','e','s','s',0};
650     static const WCHAR szActionText[] =
651         {'A','c','t','i','o','n','T','e','x','t',0};
652     DWORD log_type = 0;
653     LPWSTR message;
654     DWORD sz;
655     DWORD total_size = 0;
656     INT i;
657     INT rc;
658     char *msg;
659     int len;
660
661     TRACE("%x\n", eMessageType);
662     rc = 0;
663
664     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR)
665         log_type |= INSTALLLOGMODE_ERROR;
666     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING)
667         log_type |= INSTALLLOGMODE_WARNING;
668     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER)
669         log_type |= INSTALLLOGMODE_USER;
670     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO)
671         log_type |= INSTALLLOGMODE_INFO;
672     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA)
673         log_type |= INSTALLLOGMODE_COMMONDATA;
674     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
675         log_type |= INSTALLLOGMODE_ACTIONSTART;
676     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA)
677         log_type |= INSTALLLOGMODE_ACTIONDATA;
678     /* just a guess */
679     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS)
680         log_type |= 0x800;
681
682     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
683     {
684         static const WCHAR template_s[]=
685             {'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ',0};
686         static const WCHAR format[] = 
687             {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
688         WCHAR timet[0x100];
689         LPCWSTR action_text, action;
690         LPWSTR deformatted = NULL;
691
692         GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
693
694         action = MSI_RecordGetString(record, 1);
695         action_text = MSI_RecordGetString(record, 2);
696
697         if (!action || !action_text)
698             return IDOK;
699
700         deformat_string(package, action_text, &deformatted);
701
702         len = strlenW(timet) + strlenW(action) + strlenW(template_s);
703         if (deformatted)
704             len += strlenW(deformatted);
705         message = msi_alloc(len*sizeof(WCHAR));
706         sprintfW(message, template_s, timet, action);
707         if (deformatted)
708             strcatW(message, deformatted);
709         msi_free(deformatted);
710     }
711     else
712     {
713         INT msg_field=1;
714         message = msi_alloc(1*sizeof (WCHAR));
715         message[0]=0;
716         msg_field = MSI_RecordGetFieldCount(record);
717         for (i = 1; i <= msg_field; i++)
718         {
719             LPWSTR tmp;
720             WCHAR number[3];
721             static const WCHAR format[] = { '%','i',':',' ',0};
722             static const WCHAR space[] = { ' ',0};
723             sz = 0;
724             MSI_RecordGetStringW(record,i,NULL,&sz);
725             sz+=4;
726             total_size+=sz*sizeof(WCHAR);
727             tmp = msi_alloc(sz*sizeof(WCHAR));
728             message = msi_realloc(message,total_size*sizeof (WCHAR));
729
730             MSI_RecordGetStringW(record,i,tmp,&sz);
731
732             if (msg_field > 1)
733             {
734                 sprintfW(number,format,i);
735                 strcatW(message,number);
736             }
737             strcatW(message,tmp);
738             if (msg_field > 1)
739                 strcatW(message,space);
740
741             msi_free(tmp);
742         }
743     }
744
745     TRACE("(%p %x %x %s)\n", gUIHandlerA, gUIFilter, log_type,
746                              debugstr_w(message));
747
748     /* convert it to ASCII */
749     len = WideCharToMultiByte( CP_ACP, 0, message, -1,
750                                NULL, 0, NULL, NULL );
751     msg = msi_alloc( len );
752     WideCharToMultiByte( CP_ACP, 0, message, -1,
753                          msg, len, NULL, NULL );
754
755     if (gUIHandlerA && (gUIFilter & log_type))
756     {
757         rc = gUIHandlerA(gUIContext,eMessageType,msg);
758     }
759
760     if ((!rc) && (gszLogFile[0]) && !((eMessageType & 0xff000000) ==
761                                       INSTALLMESSAGE_PROGRESS))
762     {
763         DWORD write;
764         HANDLE log_file = CreateFileW(gszLogFile,GENERIC_WRITE, 0, NULL,
765                                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
766
767         if (log_file != INVALID_HANDLE_VALUE)
768         {
769             SetFilePointer(log_file,0, NULL, FILE_END);
770             WriteFile(log_file,msg,strlen(msg),&write,NULL);
771             WriteFile(log_file,"\n",1,&write,NULL);
772             CloseHandle(log_file);
773         }
774     }
775     msi_free( msg );
776
777     msi_free( message);
778
779     switch (eMessageType & 0xff000000)
780     {
781     case INSTALLMESSAGE_ACTIONDATA:
782         /* FIXME: format record here instead of in ui_actiondata to get the
783          * correct action data for external scripts */
784         ControlEvent_FireSubscribedEvent(package, szActionData, record);
785         break;
786     case INSTALLMESSAGE_ACTIONSTART:
787     {
788         MSIRECORD *uirow;
789         LPWSTR deformated;
790         LPCWSTR action_text = MSI_RecordGetString(record, 2);
791
792         deformat_string(package, action_text, &deformated);
793         uirow = MSI_CreateRecord(1);
794         MSI_RecordSetStringW(uirow, 1, deformated);
795         TRACE("INSTALLMESSAGE_ACTIONSTART: %s\n", debugstr_w(deformated));
796         msi_free(deformated);
797
798         ControlEvent_FireSubscribedEvent(package, szActionText, uirow);
799
800         msiobj_release(&uirow->hdr);
801         break;
802     }
803     case INSTALLMESSAGE_PROGRESS:
804         ControlEvent_FireSubscribedEvent(package, szSetProgress, record);
805         break;
806     }
807
808     return ERROR_SUCCESS;
809 }
810
811 INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType,
812                               MSIHANDLE hRecord)
813 {
814     UINT ret = ERROR_INVALID_HANDLE;
815     MSIPACKAGE *package = NULL;
816     MSIRECORD *record = NULL;
817
818     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
819     if( !package )
820         return ERROR_INVALID_HANDLE;
821
822     record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
823     if( !record )
824         goto out;
825
826     ret = MSI_ProcessMessage( package, eMessageType, record );
827
828 out:
829     msiobj_release( &package->hdr );
830     if( record )
831         msiobj_release( &record->hdr );
832
833     return ret;
834 }
835
836 /* property code */
837
838 typedef struct msi_property {
839     struct list entry;
840     LPWSTR key;
841     LPWSTR value;
842 } msi_property;
843
844 static UINT msi_prop_makehash( const WCHAR *str )
845 {
846     UINT hash = 0;
847
848     if (str==NULL)
849         return hash;
850
851     while( *str )
852     {
853         hash ^= *str++;
854         hash *= 53;
855         hash = (hash<<5) | (hash>>27);
856     }
857     return hash % PROPERTY_HASH_SIZE;
858 }
859
860 static msi_property *msi_prop_find( MSIPACKAGE *package, LPCWSTR key )
861 {
862     UINT hash = msi_prop_makehash( key );
863     msi_property *prop;
864
865     LIST_FOR_EACH_ENTRY( prop, &package->props[hash], msi_property, entry )
866         if (!lstrcmpW( prop->key, key ))
867             return prop;
868     return NULL;
869 }
870
871 static msi_property *msi_prop_add( MSIPACKAGE *package, LPCWSTR key )
872 {
873     UINT hash = msi_prop_makehash( key );
874     msi_property *prop;
875
876     prop = msi_alloc( sizeof *prop );
877     if (prop)
878     {
879         prop->key = strdupW( key );
880         prop->value = NULL;
881         list_add_head( &package->props[hash], &prop->entry );
882     }
883     return prop;
884 }
885
886 static void msi_delete_property( msi_property *prop )
887 {
888     list_remove( &prop->entry );
889     msi_free( prop->key );
890     msi_free( prop->value );
891     msi_free( prop );
892 }
893
894 static void msi_free_properties( MSIPACKAGE *package )
895 {
896     int i;
897
898     for ( i=0; i<PROPERTY_HASH_SIZE; i++ )
899     {
900         while ( !list_empty(&package->props[i]) )
901         {
902             msi_property *prop;
903             prop = LIST_ENTRY( list_head( &package->props[i] ),
904                                msi_property, entry );
905             msi_delete_property( prop );
906         }
907     }
908 }
909
910 UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue )
911 {
912     LPWSTR szwName = NULL, szwValue = NULL;
913     UINT r = ERROR_OUTOFMEMORY;
914
915     szwName = strdupAtoW( szName );
916     if( szName && !szwName )
917         goto end;
918
919     szwValue = strdupAtoW( szValue );
920     if( szValue && !szwValue )
921         goto end;
922
923     r = MsiSetPropertyW( hInstall, szwName, szwValue);
924
925 end:
926     msi_free( szwName );
927     msi_free( szwValue );
928
929     return r;
930 }
931
932 UINT MSI_SetPropertyW( MSIPACKAGE *package, LPCWSTR szName, LPCWSTR szValue)
933 {
934     msi_property *prop;
935
936     TRACE("%p %s %s\n", package, debugstr_w(szName), debugstr_w(szValue));
937
938     if (!szName)
939         return ERROR_INVALID_PARAMETER;
940
941     /* this one is weird... */
942     if (!szName[0])
943         return szValue ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
944
945     prop = msi_prop_find( package, szName );
946     if (!prop)
947         prop = msi_prop_add( package, szName );
948
949     if (!prop)
950         return ERROR_OUTOFMEMORY;
951
952     if (szValue)
953     {
954         msi_free( prop->value );
955         prop->value = strdupW( szValue );
956     }
957     else
958         msi_delete_property( prop );
959
960     return ERROR_SUCCESS;
961 }
962
963 UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
964 {
965     MSIPACKAGE *package;
966     UINT ret;
967
968     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
969     if( !package )
970         return ERROR_INVALID_HANDLE;
971     ret = MSI_SetPropertyW( package, szName, szValue);
972     msiobj_release( &package->hdr );
973     return ret;
974 }
975
976 /* internal function, not compatible with MsiGetPropertyW */
977 UINT MSI_GetPropertyW( MSIPACKAGE *package, LPCWSTR szName, 
978                        LPWSTR szValueBuf, DWORD* pchValueBuf )
979 {
980     msi_property *prop;
981     UINT r, len;
982
983     if (*pchValueBuf > 0)
984         szValueBuf[0] = 0;
985
986     prop = msi_prop_find( package, szName );
987     if (!prop)
988     {
989         *pchValueBuf = 0;
990         TRACE("property %s not found\n", debugstr_w(szName));
991         return ERROR_FUNCTION_FAILED;
992     }
993
994     if (prop->value)
995     {
996         len = lstrlenW( prop->value );
997         lstrcpynW(szValueBuf, prop->value, *pchValueBuf);
998     }
999     else
1000     {
1001         len = 1;
1002         if( *pchValueBuf > 0 )
1003             szValueBuf[0] = 0;
1004     }
1005
1006     TRACE("%s -> %s\n", debugstr_w(szName), debugstr_w(szValueBuf));
1007
1008     if ( *pchValueBuf <= len )
1009     {
1010         TRACE("have %u, need %u -> ERROR_MORE_DATA\n", *pchValueBuf, len);
1011         r = ERROR_MORE_DATA;
1012     }
1013     else
1014         r = ERROR_SUCCESS;
1015
1016     *pchValueBuf = len;
1017
1018     return r;
1019 }
1020
1021 LPWSTR msi_dup_property( MSIPACKAGE *package, LPCWSTR szName )
1022 {
1023     msi_property *prop;
1024     LPWSTR value = NULL;
1025
1026     prop = msi_prop_find( package, szName );
1027     if (prop)
1028         value = strdupW( prop->value );
1029
1030     return value;
1031 }
1032
1033 int msi_get_property_int( MSIPACKAGE *package, LPCWSTR name, int value )
1034 {
1035     msi_property *prop;
1036
1037     prop = msi_prop_find( package, name );
1038     if (prop)
1039         value = atoiW( prop->value );
1040     return value;
1041 }
1042
1043 static UINT MSI_GetProperty( MSIHANDLE handle, LPCWSTR name,
1044                              awstring *szValueBuf, DWORD* pchValueBuf )
1045 {
1046     static const WCHAR empty[] = {0};
1047     msi_property *prop;
1048     MSIPACKAGE *package;
1049     UINT r;
1050     LPCWSTR val = NULL;
1051
1052     TRACE("%lu %s %p %p\n", handle, debugstr_w(name),
1053           szValueBuf->str.w, pchValueBuf );
1054
1055     if (!name)
1056         return ERROR_INVALID_PARAMETER;
1057
1058     package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE );
1059     if (!package)
1060         return ERROR_INVALID_HANDLE;
1061
1062     prop = msi_prop_find( package, name );
1063     if (prop)
1064         val = prop->value;
1065
1066     if (!val)
1067         val = empty;
1068
1069     r = msi_strcpy_to_awstring( val, szValueBuf, pchValueBuf );
1070
1071     msiobj_release( &package->hdr );
1072
1073     return r;
1074 }
1075
1076 UINT WINAPI MsiGetPropertyA( MSIHANDLE hInstall, LPCSTR szName,
1077                              LPSTR szValueBuf, DWORD* pchValueBuf )
1078 {
1079     awstring val;
1080     LPWSTR name;
1081     UINT r;
1082
1083     val.unicode = FALSE;
1084     val.str.a = szValueBuf;
1085
1086     name = strdupAtoW( szName );
1087     if (szName && !name)
1088         return ERROR_OUTOFMEMORY;
1089
1090     r = MSI_GetProperty( hInstall, name, &val, pchValueBuf );
1091     msi_free( name );
1092     return r;
1093 }
1094
1095 UINT WINAPI MsiGetPropertyW( MSIHANDLE hInstall, LPCWSTR szName,
1096                              LPWSTR szValueBuf, DWORD* pchValueBuf )
1097 {
1098     awstring val;
1099
1100     val.unicode = TRUE;
1101     val.str.w = szValueBuf;
1102
1103     return MSI_GetProperty( hInstall, szName, &val, pchValueBuf );
1104 }