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