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