msi: Set the SourceDir and SOURCEDIR properties in the ResolveSource action.
[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
47 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48
49 static void msi_free_properties( MSIPACKAGE *package );
50
51 static void MSI_FreePackage( MSIOBJECTHDR *arg)
52 {
53     MSIPACKAGE *package= (MSIPACKAGE*) arg;
54
55     if( package->dialog )
56         msi_dialog_destroy( package->dialog );
57     ACTION_free_package_structures(package);
58
59     msi_free_properties( package );
60
61     msiobj_release( &package->db->hdr );
62 }
63
64 static UINT clone_properties( MSIPACKAGE *package )
65 {
66     MSIQUERY * view = NULL;
67     UINT rc;
68     static const WCHAR Query[] = {
69        'S','E','L','E','C','T',' ','*',' ',
70        'F','R','O','M',' ','`','P','r','o','p','e','r','t','y','`',0};
71
72     /* clone the existing properties */
73     rc = MSI_DatabaseOpenViewW( package->db, Query, &view );
74     if (rc != ERROR_SUCCESS)
75         return rc;
76
77     rc = MSI_ViewExecute(view, 0);
78     if (rc != ERROR_SUCCESS)
79     {
80         MSI_ViewClose(view);
81         msiobj_release(&view->hdr);
82         return rc;
83     }
84     while (1)
85     {
86         MSIRECORD * row;
87         LPCWSTR name, value;
88
89         rc = MSI_ViewFetch(view,&row);
90         if (rc != ERROR_SUCCESS)
91             break;
92
93         name = MSI_RecordGetString( row, 1 );
94         value = MSI_RecordGetString( row, 2 );
95         MSI_SetPropertyW( package, name, value );
96
97         msiobj_release( &row->hdr );
98     }
99     MSI_ViewClose(view);
100     msiobj_release(&view->hdr);
101
102     return rc;
103 }
104
105 /*
106  * set_installed_prop
107  *
108  * Sets the "Installed" property to indicate that
109  *  the product is installed for the current user.
110  */
111 static UINT set_installed_prop( MSIPACKAGE *package )
112 {
113     static const WCHAR szInstalled[] = {
114         'I','n','s','t','a','l','l','e','d',0 };
115     WCHAR val[2] = { '1', 0 };
116     HKEY hkey = 0;
117     UINT r;
118
119     r = MSIREG_OpenUninstallKey( package->ProductCode, &hkey, FALSE );
120     if (r == ERROR_SUCCESS)
121     {
122         RegCloseKey( hkey );
123         MSI_SetPropertyW( package, szInstalled, val );
124     }
125
126     return r;
127 }
128
129 /*
130  * There are a whole slew of these we need to set
131  *
132  *
133 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
134  */
135 static VOID set_installer_properties(MSIPACKAGE *package)
136 {
137     WCHAR pth[MAX_PATH];
138     WCHAR *ptr;
139     OSVERSIONINFOA OSVersion;
140     MEMORYSTATUSEX msex;
141     DWORD verval;
142     WCHAR verstr[10], bufstr[20];
143     HDC dc;
144
145     static const WCHAR cszbs[]={'\\',0};
146     static const WCHAR CFF[] = 
147 {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
148     static const WCHAR PFF[] = 
149 {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
150     static const WCHAR CADF[] = 
151 {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
152     static const WCHAR FaF[] = 
153 {'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
154     static const WCHAR FoF[] = 
155 {'F','o','n','t','s','F','o','l','d','e','r',0};
156     static const WCHAR SendTF[] = 
157 {'S','e','n','d','T','o','F','o','l','d','e','r',0};
158     static const WCHAR SMF[] = 
159 {'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0};
160     static const WCHAR StF[] = 
161 {'S','t','a','r','t','u','p','F','o','l','d','e','r',0};
162     static const WCHAR TemplF[] = 
163 {'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0};
164     static const WCHAR DF[] = 
165 {'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
166     static const WCHAR PMF[] = 
167 {'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0};
168     static const WCHAR ATF[] = 
169 {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
170     static const WCHAR ADF[] = 
171 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
172     static const WCHAR SF[] = 
173 {'S','y','s','t','e','m','F','o','l','d','e','r',0};
174     static const WCHAR SF16[] = 
175 {'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0};
176     static const WCHAR LADF[] = 
177 {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
178     static const WCHAR MPF[] = 
179 {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
180     static const WCHAR PF[] = 
181 {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
182     static const WCHAR WF[] = 
183 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
184     static const WCHAR WV[] = 
185 {'W','i','n','d','o','w','s','V','o','l','u','m','e',0};
186     static const WCHAR TF[]=
187 {'T','e','m','p','F','o','l','d','e','r',0};
188     static const WCHAR szAdminUser[] =
189 {'A','d','m','i','n','U','s','e','r',0};
190     static const WCHAR szPriv[] =
191 {'P','r','i','v','i','l','e','g','e','d',0};
192     static const WCHAR szOne[] =
193 {'1',0};
194     static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 };
195     static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 };
196     static const WCHAR szFormat[] = {'%','l','i',0};
197     static const WCHAR szWinBuild[] =
198 {'W','i','n','d','o','w','s','B','u','i','l','d', 0 };
199     static const WCHAR szSPL[] = 
200 {'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 };
201     static const WCHAR szSix[] = {'6',0 };
202
203     static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 };
204     static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 };
205     static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0};
206 /* Screen properties */
207     static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
208     static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
209     static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0};
210     static const WCHAR szScreenFormat[] = {'%','d',0};
211     static const WCHAR szIntel[] = { 'I','n','t','e','l',0 };
212     static const WCHAR szAllUsers[] = { 'A','L','L','U','S','E','R','S',0 };
213     SYSTEM_INFO sys_info;
214
215     /*
216      * Other things that probably should be set:
217      *
218      * SystemLanguageID ComputerName UserLanguageID LogonUser VirtualMemory
219      * Intel ShellAdvSupport DefaultUIFont VersionDatabase PackagecodeChanging
220      * ProductState CaptionHeight BorderTop BorderSide TextHeight
221      * RedirectedDllSupport Time Date Privileged
222      */
223
224     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
225     strcatW(pth,cszbs);
226     MSI_SetPropertyW(package, CFF, pth);
227
228     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
229     strcatW(pth,cszbs);
230     MSI_SetPropertyW(package, PFF, pth);
231
232     SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
233     strcatW(pth,cszbs);
234     MSI_SetPropertyW(package, CADF, pth);
235
236     SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth);
237     strcatW(pth,cszbs);
238     MSI_SetPropertyW(package, FaF, pth);
239
240     SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth);
241     strcatW(pth,cszbs);
242     MSI_SetPropertyW(package, FoF, pth);
243
244     SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth);
245     strcatW(pth,cszbs);
246     MSI_SetPropertyW(package, SendTF, pth);
247
248     SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth);
249     strcatW(pth,cszbs);
250     MSI_SetPropertyW(package, SMF, pth);
251
252     SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth);
253     strcatW(pth,cszbs);
254     MSI_SetPropertyW(package, StF, pth);
255
256     SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth);
257     strcatW(pth,cszbs);
258     MSI_SetPropertyW(package, TemplF, pth);
259
260     SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth);
261     strcatW(pth,cszbs);
262     MSI_SetPropertyW(package, DF, pth);
263
264     SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth);
265     strcatW(pth,cszbs);
266     MSI_SetPropertyW(package, PMF, pth);
267
268     SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
269     strcatW(pth,cszbs);
270     MSI_SetPropertyW(package, ATF, pth);
271
272     SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
273     strcatW(pth,cszbs);
274     MSI_SetPropertyW(package, ADF, pth);
275
276     SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
277     strcatW(pth,cszbs);
278     MSI_SetPropertyW(package, SF, pth);
279     MSI_SetPropertyW(package, SF16, pth);
280
281     SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
282     strcatW(pth,cszbs);
283     MSI_SetPropertyW(package, LADF, pth);
284
285     SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
286     strcatW(pth,cszbs);
287     MSI_SetPropertyW(package, MPF, pth);
288
289     SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
290     strcatW(pth,cszbs);
291     MSI_SetPropertyW(package, PF, pth);
292
293     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
294     strcatW(pth,cszbs);
295     MSI_SetPropertyW(package, WF, pth);
296     
297     /* Physical Memory is specified in MB. Using total amount. */
298     msex.dwLength = sizeof(msex);
299     GlobalMemoryStatusEx( &msex );
300     sprintfW( bufstr, szScreenFormat, (int)(msex.ullTotalPhys/1024/1024));
301     MSI_SetPropertyW(package, szPhysicalMemory, bufstr);
302
303     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
304     ptr = strchrW(pth,'\\');
305     if (ptr)
306         *(ptr+1) = 0;
307     MSI_SetPropertyW(package, WV, pth);
308     
309     GetTempPathW(MAX_PATH,pth);
310     MSI_SetPropertyW(package, TF, pth);
311
312
313     /* in a wine environment the user is always admin and privileged */
314     MSI_SetPropertyW(package,szAdminUser,szOne);
315     MSI_SetPropertyW(package,szPriv,szOne);
316     MSI_SetPropertyW(package, szAllUsers, szOne);
317
318     /* set the os things */
319     OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
320     GetVersionExA(&OSVersion);
321     verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
322     sprintfW(verstr,szFormat,verval);
323     switch (OSVersion.dwPlatformId)
324     {
325         case VER_PLATFORM_WIN32_WINDOWS:    
326             MSI_SetPropertyW(package,v9x,verstr);
327             break;
328         case VER_PLATFORM_WIN32_NT:
329             MSI_SetPropertyW(package,vNT,verstr);
330             break;
331     }
332     sprintfW(verstr,szFormat,OSVersion.dwBuildNumber);
333     MSI_SetPropertyW(package,szWinBuild,verstr);
334     /* just fudge this */
335     MSI_SetPropertyW(package,szSPL,szSix);
336
337     sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION);
338     MSI_SetPropertyW( package, szVersionMsi, bufstr );
339
340     GetSystemInfo( &sys_info );
341     if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
342     {
343         sprintfW( bufstr, szScreenFormat, sys_info.wProcessorLevel );
344         MSI_SetPropertyW( package, szIntel, bufstr );
345     }
346
347     /* Screen properties. */
348     dc = GetDC(0);
349     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, HORZRES ) );
350     MSI_SetPropertyW( package, szScreenX, bufstr );
351     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, VERTRES ));
352     MSI_SetPropertyW( package, szScreenY, bufstr );
353     sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, BITSPIXEL ));
354     MSI_SetPropertyW( package, szColorBits, bufstr );
355     ReleaseDC(0, dc);
356 }
357
358 static UINT msi_get_word_count( MSIPACKAGE *package )
359 {
360     UINT rc;
361     INT word_count;
362     MSIHANDLE suminfo;
363     MSIHANDLE hdb = alloc_msihandle( &package->db->hdr );
364
365     if (!hdb) {
366         ERR("Unable to allocate handle\n");
367         return 0;
368     }
369     rc = MsiGetSummaryInformationW( hdb, NULL, 0, &suminfo );
370     MsiCloseHandle(hdb);
371     if (rc != ERROR_SUCCESS)
372     {
373         ERR("Unable to open Summary Information\n");
374         return 0;
375     }
376
377     rc = MsiSummaryInfoGetPropertyW( suminfo, PID_WORDCOUNT, NULL,
378                                      &word_count, NULL, NULL, NULL );
379     if (rc != ERROR_SUCCESS)
380     {
381         ERR("Unable to query word count\n");
382         MsiCloseHandle(suminfo);
383         return 0;
384     }
385
386     MsiCloseHandle(suminfo);
387     return word_count;
388 }
389
390 MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db )
391 {
392     static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 };
393     static const WCHAR szpi[] = {'%','i',0};
394     static const WCHAR szProductCode[] = {
395         'P','r','o','d','u','c','t','C','o','d','e',0};
396     MSIPACKAGE *package = NULL;
397     WCHAR uilevel[10];
398     int i;
399
400     TRACE("%p\n", db);
401
402     package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
403                                MSI_FreePackage );
404     if( package )
405     {
406         msiobj_addref( &db->hdr );
407
408         package->db = db;
409         list_init( &package->components );
410         list_init( &package->features );
411         list_init( &package->files );
412         list_init( &package->tempfiles );
413         list_init( &package->folders );
414         package->ActionFormat = NULL;
415         package->LastAction = NULL;
416         package->dialog = NULL;
417         package->next_dialog = NULL;
418         list_init( &package->subscriptions );
419         list_init( &package->appids );
420         list_init( &package->classes );
421         list_init( &package->mimes );
422         list_init( &package->extensions );
423         list_init( &package->progids );
424         list_init( &package->RunningActions );
425
426         package->WordCount = msi_get_word_count( package );
427         package->PackagePath = strdupW( db->path );
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 }