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