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