msi: automation: Database::SummaryInformation is a propget, not a method.
[wine] / dlls / msi / custom.c
1 /*
2  * Custom Action processing for the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 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 COBJMACROS
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "msidefs.h"
28 #include "msipriv.h"
29 #include "winuser.h"
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msi);
34
35 #define CUSTOM_ACTION_TYPE_MASK 0x3F
36 static const WCHAR c_collen[] = {'C',':','\\',0};
37 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
38
39 typedef struct tagMSIRUNNINGACTION
40 {
41     struct list entry;
42     HANDLE handle;
43     BOOL   process;
44     LPWSTR name;
45 } MSIRUNNINGACTION;
46
47 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
48                                LPCWSTR target, const INT type, LPCWSTR action);
49 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
50                                LPCWSTR target, const INT type, LPCWSTR action);
51 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
52                                 LPCWSTR target, const INT type, LPCWSTR action);
53 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
54                                 LPCWSTR target, const INT type, LPCWSTR action);
55 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
56                                 LPCWSTR target, const INT type, LPCWSTR action);
57 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
58                                 LPCWSTR target, const INT type, LPCWSTR action);
59 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
60                                 LPCWSTR target, const INT type, LPCWSTR action);
61 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
62                                 LPCWSTR target, const INT type, LPCWSTR action);
63 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
64                                 LPCWSTR target, const INT type, LPCWSTR action);
65 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
66                                 LPCWSTR target, const INT type, LPCWSTR action);
67 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
68                                 LPCWSTR target, const INT type, LPCWSTR action);
69 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
70                                 LPCWSTR target, const INT type, LPCWSTR action);
71
72 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
73
74 static CRITICAL_SECTION msi_custom_action_cs;
75 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
76 {
77     0, 0, &msi_custom_action_cs,
78     { &msi_custom_action_cs_debug.ProcessLocksList,
79       &msi_custom_action_cs_debug.ProcessLocksList },
80       0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
81 };
82 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
83
84 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
85
86 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
87 {
88     if (!package->script)
89         return TRUE;
90
91     if ((options & msidbCustomActionTypeClientRepeat) ==
92             msidbCustomActionTypeClientRepeat)
93     {
94         if (!(package->script->InWhatSequence & SEQUENCE_UI &&
95             package->script->InWhatSequence & SEQUENCE_EXEC))
96         {
97             TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
98             return FALSE;
99         }
100     }
101     else if (options & msidbCustomActionTypeFirstSequence)
102     {
103         if (package->script->InWhatSequence & SEQUENCE_UI &&
104             package->script->InWhatSequence & SEQUENCE_EXEC )
105         {
106             TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
107             return FALSE;
108         }
109     }
110     else if (options & msidbCustomActionTypeOncePerProcess)
111     {
112         if (check_unique_action(package,action))
113         {
114             TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
115             return FALSE;
116         }
117         else
118             register_unique_action(package,action);
119     }
120
121     return TRUE;
122 }
123
124 /* stores the CustomActionData before the action:
125  *     [CustomActionData]Action
126  */
127 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata)
128 {
129     LPWSTR deferred;
130     DWORD len;
131
132     static const WCHAR begin[] = {'[',0};
133     static const WCHAR end[] = {']',0};
134
135     if (!actiondata)
136         return strdupW(action);
137
138     len = lstrlenW(action) + lstrlenW(actiondata) + 3;
139     deferred = msi_alloc(len * sizeof(WCHAR));
140
141     lstrcpyW(deferred, begin);
142     lstrcatW(deferred, actiondata);
143     lstrcatW(deferred, end);
144     lstrcatW(deferred, action);
145
146     return deferred;
147 }
148
149 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
150 {
151     UINT rc = ERROR_SUCCESS;
152     MSIRECORD * row = 0;
153     static const WCHAR ExecSeqQuery[] =
154     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
155      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
156      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
157      '=',' ','\'','%','s','\'',0};
158     UINT type;
159     LPCWSTR source, target;
160     LPWSTR ptr, deferred_data = NULL;
161     LPWSTR action_copy = strdupW(action);
162     WCHAR *deformated=NULL;
163
164     /* deferred action: [CustomActionData]Action */
165     if ((ptr = strchrW(action_copy, ']')))
166     {
167         deferred_data = action_copy + 1;
168         *ptr = '\0';
169         action = ptr + 1;
170     }
171
172     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
173     if (!row)
174     {
175         msi_free(action_copy);
176         return ERROR_CALL_NOT_IMPLEMENTED;
177     }
178
179     type = MSI_RecordGetInteger(row,2);
180
181     source = MSI_RecordGetString(row,3);
182     target = MSI_RecordGetString(row,4);
183
184     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
185           debugstr_w(source), debugstr_w(target));
186
187     /* handle some of the deferred actions */
188     if (type & msidbCustomActionTypeTSAware)
189         FIXME("msidbCustomActionTypeTSAware not handled\n");
190
191     if (type & msidbCustomActionTypeInScript)
192     {
193         if (type & msidbCustomActionTypeNoImpersonate)
194             FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
195
196         if (type & msidbCustomActionTypeRollback)
197         {
198             FIXME("Rollback only action... rollbacks not supported yet\n");
199             schedule_action(package, ROLLBACK_SCRIPT, action);
200             rc = ERROR_SUCCESS;
201             goto end;
202         }
203         if (!execute)
204         {
205             LPWSTR actiondata = msi_dup_property(package, action);
206             LPWSTR deferred = msi_get_deferred_action(action, actiondata);
207
208             if (type & msidbCustomActionTypeCommit)
209             {
210                 TRACE("Deferring Commit Action!\n");
211                 schedule_action(package, COMMIT_SCRIPT, deferred);
212             }
213             else
214             {
215                 TRACE("Deferring Action!\n");
216                 schedule_action(package, INSTALL_SCRIPT, deferred);
217             }
218
219             rc = ERROR_SUCCESS;
220             msi_free(deferred);
221             goto end;
222         }
223         else
224         {
225             /*Set ActionData*/
226
227             static const WCHAR szActionData[] = {
228             'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
229             static const WCHAR szBlank[] = {0};
230             LPWSTR actiondata = msi_dup_property( package, action );
231             if (deferred_data)
232                 MSI_SetPropertyW(package,szActionData,deferred_data);
233             else if (actiondata)
234                 MSI_SetPropertyW(package,szActionData,actiondata);
235             else
236                 MSI_SetPropertyW(package,szActionData,szBlank);
237             msi_free(actiondata);
238         }
239     }
240     else if (!check_execution_scheduling_options(package,action,type))
241     {
242         rc = ERROR_SUCCESS;
243         goto end;
244     }
245
246     switch (type & CUSTOM_ACTION_TYPE_MASK)
247     {
248         case 1: /* DLL file stored in a Binary table stream */
249             rc = HANDLE_CustomType1(package,source,target,type,action);
250             break;
251         case 2: /* EXE file stored in a Binary table stream */
252             rc = HANDLE_CustomType2(package,source,target,type,action);
253             break;
254         case 18: /*EXE file installed with package */
255             rc = HANDLE_CustomType18(package,source,target,type,action);
256             break;
257         case 19: /* Error that halts install */
258             rc = HANDLE_CustomType19(package,source,target,type,action);
259             break;
260         case 17:
261             rc = HANDLE_CustomType17(package,source,target,type,action);
262             break;
263         case 23: /* installs another package in the source tree */
264             deformat_string(package,target,&deformated);
265             rc = HANDLE_CustomType23(package,source,deformated,type,action);
266             break;
267         case 50: /*EXE file specified by a property value */
268             rc = HANDLE_CustomType50(package,source,target,type,action);
269             break;
270         case 34: /*EXE to be run in specified directory */
271             rc = HANDLE_CustomType34(package,source,target,type,action);
272             break;
273         case 35: /* Directory set with formatted text. */
274             deformat_string(package,target,&deformated);
275             MSI_SetTargetPathW(package, source, deformated);
276             msi_free(deformated);
277             break;
278         case 51: /* Property set with formatted text. */
279             deformat_string(package,target,&deformated);
280             rc = MSI_SetPropertyW(package,source,deformated);
281             msi_free(deformated);
282             break;
283         case 37: /* JScript/VBScript text stored in target column. */
284         case 38:
285             rc = HANDLE_CustomType37_38(package,source,target,type,action);
286             break;
287         case 5:
288         case 6: /* JScript/VBScript file stored in a Binary table stream. */
289             rc = HANDLE_CustomType5_6(package,source,target,type,action);
290             break;
291         case 21: /* JScript/VBScript file installed with the product. */
292         case 22:
293             rc = HANDLE_CustomType21_22(package,source,target,type,action);
294             break;
295         case 53: /* JScript/VBScript text specified by a property value. */
296         case 54:
297             rc = HANDLE_CustomType53_54(package,source,target,type,action);
298             break;
299         default:
300             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
301              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
302              debugstr_w(target));
303     }
304
305 end:
306     msi_free(action_copy);
307     msiobj_release(&row->hdr);
308     return rc;
309 }
310
311
312 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
313                                 LPWSTR tmp_file)
314 {
315     static const WCHAR query[] = {
316         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
317         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
318         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
319     MSIRECORD *row = 0;
320     HANDLE file;
321     CHAR buffer[1024];
322     static const WCHAR f1[] = {'m','s','i',0};
323     WCHAR fmt[MAX_PATH];
324     DWORD sz = MAX_PATH;
325     UINT r;
326
327     if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
328         GetTempPathW(MAX_PATH, fmt);
329
330     if (GetTempFileNameW(fmt, f1, 0, tmp_file) == 0)
331     {
332         TRACE("Unable to create file\n");
333         return ERROR_FUNCTION_FAILED;
334     }
335     track_tempfile(package, tmp_file);
336
337     row = MSI_QueryGetRecord(package->db, query, source);
338     if (!row)
339         return ERROR_FUNCTION_FAILED;
340
341     /* write out the file */
342     file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
343                        FILE_ATTRIBUTE_NORMAL, NULL);
344     if (file == INVALID_HANDLE_VALUE)
345         r = ERROR_FUNCTION_FAILED;
346     else
347     {
348         do
349         {
350             DWORD write;
351             sz = sizeof buffer;
352             r = MSI_RecordReadStream(row, 2, buffer, &sz);
353             if (r != ERROR_SUCCESS)
354             {
355                 ERR("Failed to get stream\n");
356                 break;
357             }
358             WriteFile(file, buffer, sz, &write, NULL);
359         } while (sz == sizeof buffer);
360         CloseHandle(file);
361     }
362
363     msiobj_release(&row->hdr);
364
365     return r;
366 }
367
368 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
369                                 BOOL process, LPCWSTR name)
370 {
371     MSIRUNNINGACTION *action;
372
373     action = msi_alloc( sizeof(MSIRUNNINGACTION) );
374
375     action->handle = Handle;
376     action->process = process;
377     action->name = strdupW(name);
378
379     list_add_tail( &package->RunningActions, &action->entry );
380 }
381
382 static UINT custom_get_process_return( HANDLE process )
383 {
384     DWORD rc = 0;
385
386     GetExitCodeProcess( process, &rc );
387     if (rc != 0)
388         return ERROR_FUNCTION_FAILED;
389     return ERROR_SUCCESS;
390 }
391
392 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
393 {
394     DWORD rc = 0;
395
396     GetExitCodeThread( thread, &rc );
397
398     switch (rc)
399     {
400     case ERROR_FUNCTION_NOT_CALLED:
401     case ERROR_SUCCESS:
402     case ERROR_INSTALL_USEREXIT:
403     case ERROR_INSTALL_FAILURE:
404         return rc;
405     case ERROR_NO_MORE_ITEMS:
406         return ERROR_SUCCESS;
407     case ERROR_INSTALL_SUSPEND:
408         ACTION_ForceReboot( package );
409         return ERROR_SUCCESS;
410     default:
411         ERR("Invalid Return Code %d\n",rc);
412         return ERROR_INSTALL_FAILURE;
413     }
414 }
415
416 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
417                            HANDLE ProcessHandle, LPCWSTR name)
418 {
419     UINT rc = ERROR_SUCCESS;
420
421     if (!(type & msidbCustomActionTypeAsync))
422     {
423         TRACE("waiting for %s\n", debugstr_w(name));
424
425         msi_dialog_check_messages(ProcessHandle);
426
427         if (!(type & msidbCustomActionTypeContinue))
428             rc = custom_get_process_return(ProcessHandle);
429
430         CloseHandle(ProcessHandle);
431     }
432     else
433     {
434         TRACE("%s running in background\n", debugstr_w(name));
435
436         if (!(type & msidbCustomActionTypeContinue))
437             file_running_action(package, ProcessHandle, TRUE, name);
438         else
439             CloseHandle(ProcessHandle);
440     }
441
442     return rc;
443 }
444
445 typedef struct _msi_custom_action_info {
446     struct list entry;
447     MSIPACKAGE *package;
448     LPWSTR source;
449     LPWSTR target;
450     HANDLE handle;
451     LPWSTR action;
452     INT type;
453     GUID guid;
454 } msi_custom_action_info;
455
456 static void free_custom_action_data( msi_custom_action_info *info )
457 {
458     EnterCriticalSection( &msi_custom_action_cs );
459     list_remove( &info->entry );
460     LeaveCriticalSection( &msi_custom_action_cs );
461     if (info->handle)
462         CloseHandle( info->handle );
463     msi_free( info->action );
464     msi_free( info->source );
465     msi_free( info->target );
466     msiobj_release( &info->package->hdr );
467     msi_free( info );
468 }
469
470 static UINT wait_thread_handle( msi_custom_action_info *info )
471 {
472     UINT rc = ERROR_SUCCESS;
473
474     if (!(info->type & msidbCustomActionTypeAsync))
475     {
476         TRACE("waiting for %s\n", debugstr_w( info->action ));
477
478         msi_dialog_check_messages( info->handle );
479
480         if (!(info->type & msidbCustomActionTypeContinue))
481             rc = custom_get_thread_return( info->package, info->handle );
482
483         free_custom_action_data( info );
484     }
485     else
486     {
487         TRACE("%s running in background\n", debugstr_w( info->action ));
488     }
489
490     return rc;
491 }
492
493 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
494 {
495     msi_custom_action_info *info;
496     BOOL found = FALSE;
497
498     EnterCriticalSection( &msi_custom_action_cs );
499
500     LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
501     {
502         if (IsEqualGUID( &info->guid, guid ))
503         {
504             found = TRUE;
505             break;
506         }
507     }
508
509     LeaveCriticalSection( &msi_custom_action_cs );
510
511     if (!found)
512         return NULL;
513
514     return info;
515 }
516
517 static void handle_msi_break( LPCWSTR target )
518 {
519     LPWSTR msg;
520     WCHAR val[MAX_PATH];
521
522     static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
523     static const WCHAR WindowsInstaller[] = {
524         'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
525     };
526
527     static const WCHAR format[] = {
528         'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
529         'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
530         'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
531         't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
532         'a','n','d',' ','p','r','e','s','s',' ','O','K',0
533     };
534
535     if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
536         return;
537
538     if( lstrcmpiW( val, target ))
539         return;
540
541     msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
542     if (!msg)
543         return;
544
545     wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
546     MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
547     msi_free(msg);
548     DebugBreak();
549 }
550
551 static DWORD WINAPI ACTION_CallDllFunction( const GUID *guid )
552 {
553     msi_custom_action_info *info;
554     MsiCustomActionEntryPoint fn;
555     MSIHANDLE hPackage;
556     HANDLE hModule;
557     LPSTR proc;
558     UINT r = ERROR_FUNCTION_FAILED;
559
560     info = find_action_by_guid( guid );
561     if (!info)
562     {
563         ERR("failed to find action %s\n", debugstr_guid( guid) );
564         return r;
565     }
566
567     TRACE("%s %s\n", debugstr_w( info->source ), debugstr_w( info->target ) );
568
569     hModule = LoadLibraryW( info->source );
570     if (!hModule)
571     {
572         ERR("failed to load dll %s\n", debugstr_w( info->source ) );
573         return r;
574     }
575
576     proc = strdupWtoA( info->target );
577     fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
578     msi_free( proc );
579     if (fn)
580     {
581         hPackage = alloc_msihandle( &info->package->hdr );
582         if (hPackage)
583         {
584             TRACE("calling %s\n", debugstr_w( info->target ) );
585             handle_msi_break( info->target );
586             r = fn( hPackage );
587             MsiCloseHandle( hPackage );
588         }
589         else
590             ERR("failed to create handle for %p\n", info->package );
591     }
592     else
593         ERR("GetProcAddress(%s) failed\n", debugstr_w( info->target ) );
594
595     FreeLibrary(hModule);
596
597     if (info->type & msidbCustomActionTypeAsync &&
598         info->type & msidbCustomActionTypeContinue)
599         free_custom_action_data( info );
600
601     return r;
602 }
603
604 static DWORD WINAPI DllThread( LPVOID arg )
605 {
606     LPGUID guid = arg;
607     DWORD rc = 0;
608
609     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
610
611     rc = ACTION_CallDllFunction( guid );
612
613     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
614
615     MsiCloseAllHandles();
616     return rc;
617 }
618
619 static DWORD WINAPI ACTION_CAInstallPackage(const GUID *guid)
620 {
621     msi_custom_action_info *info;
622     UINT r = ERROR_FUNCTION_FAILED;
623     INSTALLUILEVEL old_level;
624
625     info = find_action_by_guid(guid);
626     if (!info)
627     {
628         ERR("failed to find action %s\n", debugstr_guid(guid));
629         return r;
630     }
631
632     old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
633     r = MsiInstallProductW(info->source, info->target);
634     MsiSetInternalUI(old_level, NULL);
635
636     return r;
637 }
638
639 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
640 {
641     LPGUID guid = arg;
642     DWORD rc;
643
644     TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
645
646     rc = ACTION_CAInstallPackage(guid);
647
648     TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
649
650     MsiCloseAllHandles();
651     return rc;
652 }
653
654 static msi_custom_action_info *do_msidbCustomActionTypeDll(
655     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
656 {
657     msi_custom_action_info *info;
658
659     info = msi_alloc( sizeof *info );
660     if (!info)
661         return NULL;
662
663     msiobj_addref( &package->hdr );
664     info->package = package;
665     info->type = type;
666     info->target = strdupW( target );
667     info->source = strdupW( source );
668     info->action = strdupW( action );
669     CoCreateGuid( &info->guid );
670
671     EnterCriticalSection( &msi_custom_action_cs );
672     list_add_tail( &msi_pending_custom_actions, &info->entry );
673     LeaveCriticalSection( &msi_custom_action_cs );
674
675     info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
676     if (!info->handle)
677     {
678         free_custom_action_data( info );
679         return NULL;
680     }
681
682     return info;
683 }
684
685 static msi_custom_action_info *do_msidbCAConcurrentInstall(
686     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
687 {
688     msi_custom_action_info *info;
689
690     info = msi_alloc( sizeof *info );
691     if (!info)
692         return NULL;
693
694     msiobj_addref( &package->hdr );
695     info->package = package;
696     info->type = type;
697     info->target = strdupW( target );
698     info->source = strdupW( source );
699     info->action = strdupW( action );
700     CoCreateGuid( &info->guid );
701
702     EnterCriticalSection( &msi_custom_action_cs );
703     list_add_tail( &msi_pending_custom_actions, &info->entry );
704     LeaveCriticalSection( &msi_custom_action_cs );
705
706     info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
707     if (!info->handle)
708     {
709         free_custom_action_data( info );
710         return NULL;
711     }
712
713     return info;
714 }
715
716 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
717                                 LPCWSTR target, const INT type, LPCWSTR action)
718 {
719     msi_custom_action_info *info;
720     WCHAR package_path[MAX_PATH];
721     DWORD size;
722
723     static const WCHAR backslash[] = {'\\',0};
724
725     MSI_GetPropertyW(package, cszSourceDir, package_path, &size);
726     lstrcatW(package_path, backslash);
727     lstrcatW(package_path, source);
728
729     if (GetFileAttributesW(package_path) == INVALID_FILE_ATTRIBUTES)
730     {
731         ERR("Source package does not exist: %s\n", debugstr_w(package_path));
732         return ERROR_FUNCTION_FAILED;
733     }
734
735     TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
736
737     info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
738
739     return wait_thread_handle(info);
740 }
741
742 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
743                                LPCWSTR target, const INT type, LPCWSTR action)
744 {
745     msi_custom_action_info *info;
746     WCHAR tmp_file[MAX_PATH];
747     UINT r;
748
749     r = store_binary_to_temp(package, source, tmp_file);
750     if (r != ERROR_SUCCESS)
751         return r;
752
753     TRACE("Calling function %s from %s\n",debugstr_w(target),
754           debugstr_w(tmp_file));
755
756     if (!strchrW(tmp_file,'.'))
757     {
758         static const WCHAR dot[]={'.',0};
759         strcatW(tmp_file,dot);
760     }
761
762     info = do_msidbCustomActionTypeDll( package, type, tmp_file, target, action );
763
764     return wait_thread_handle( info );
765 }
766
767 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
768                                LPCWSTR target, const INT type, LPCWSTR action)
769 {
770     WCHAR tmp_file[MAX_PATH];
771     STARTUPINFOW si;
772     PROCESS_INFORMATION info;
773     BOOL rc;
774     INT len;
775     WCHAR *deformated = NULL;
776     WCHAR *cmd;
777     static const WCHAR spc[] = {' ',0};
778     UINT r;
779
780     memset(&si,0,sizeof(STARTUPINFOW));
781
782     r = store_binary_to_temp(package, source, tmp_file);
783     if (r != ERROR_SUCCESS)
784         return r;
785
786     deformat_string(package,target,&deformated);
787
788     len = strlenW(tmp_file)+2;
789
790     if (deformated)
791         len += strlenW(deformated);
792
793     cmd = msi_alloc(sizeof(WCHAR)*len);
794
795     strcpyW(cmd,tmp_file);
796     if (deformated)
797     {
798         strcatW(cmd,spc);
799         strcatW(cmd,deformated);
800
801         msi_free(deformated);
802     }
803
804     TRACE("executing exe %s\n", debugstr_w(cmd));
805
806     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
807                   c_collen, &si, &info);
808     msi_free(cmd);
809
810     if ( !rc )
811     {
812         ERR("Unable to execute command %s\n", debugstr_w(cmd));
813         return ERROR_SUCCESS;
814     }
815     CloseHandle( info.hThread );
816
817     r = wait_process_handle(package, type, info.hProcess, action);
818
819     return r;
820 }
821
822 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
823                                 LPCWSTR target, const INT type, LPCWSTR action)
824 {
825     msi_custom_action_info *info;
826     MSIFILE *file;
827
828     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
829
830     file = get_loaded_file( package, source );
831     if (!file)
832     {
833         ERR("invalid file key %s\n", debugstr_w( source ));
834         return ERROR_FUNCTION_FAILED;
835     }
836
837     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
838
839     return wait_thread_handle( info );
840 }
841
842 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
843                                 LPCWSTR target, const INT type, LPCWSTR action)
844 {
845     STARTUPINFOW si;
846     PROCESS_INFORMATION info;
847     BOOL rc;
848     WCHAR *deformated;
849     WCHAR *cmd;
850     INT len;
851     static const WCHAR spc[] = {' ',0};
852     MSIFILE *file;
853
854     memset(&si,0,sizeof(STARTUPINFOW));
855
856     file = get_loaded_file(package,source);
857     if( !file )
858         return ERROR_FUNCTION_FAILED;
859
860     len = lstrlenW( file->TargetPath );
861
862     deformat_string(package,target,&deformated);
863     if (deformated)
864         len += strlenW(deformated);
865     len += 2;
866
867     cmd = msi_alloc(len * sizeof(WCHAR));
868
869     lstrcpyW( cmd, file->TargetPath);
870     if (deformated)
871     {
872         strcatW(cmd, spc);
873         strcatW(cmd, deformated);
874
875         msi_free(deformated);
876     }
877
878     TRACE("executing exe %s\n", debugstr_w(cmd));
879
880     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
881                   c_collen, &si, &info);
882
883     if ( !rc )
884     {
885         ERR("Unable to execute command %s\n", debugstr_w(cmd));
886         msi_free(cmd);
887         return ERROR_SUCCESS;
888     }
889     msi_free(cmd);
890     CloseHandle( info.hThread );
891
892     return wait_process_handle(package, type, info.hProcess, action);
893 }
894
895 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
896                                 LPCWSTR target, const INT type, LPCWSTR action)
897 {
898     static const WCHAR query[] = {
899       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
900       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
901       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
902       '%','s',0
903     };
904     MSIRECORD *row = 0;
905     LPWSTR deformated = NULL;
906
907     deformat_string( package, target, &deformated );
908
909     /* first try treat the error as a number */
910     row = MSI_QueryGetRecord( package->db, query, deformated );
911     if( row )
912     {
913         LPCWSTR error = MSI_RecordGetString( row, 1 );
914         MessageBoxW( NULL, error, NULL, MB_OK );
915         msiobj_release( &row->hdr );
916     }
917     else
918         MessageBoxW( NULL, deformated, NULL, MB_OK );
919
920     msi_free( deformated );
921
922     return ERROR_FUNCTION_FAILED;
923 }
924
925 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
926                                 LPCWSTR target, const INT type, LPCWSTR action)
927 {
928     STARTUPINFOW si;
929     PROCESS_INFORMATION info;
930     WCHAR *prop;
931     BOOL rc;
932     WCHAR *deformated;
933     WCHAR *cmd;
934     INT len;
935     static const WCHAR spc[] = {' ',0};
936
937     memset(&si,0,sizeof(STARTUPINFOW));
938     memset(&info,0,sizeof(PROCESS_INFORMATION));
939
940     prop = msi_dup_property( package, source );
941     if (!prop)
942         return ERROR_SUCCESS;
943
944     deformat_string(package,target,&deformated);
945     len = strlenW(prop) + 2;
946     if (deformated)
947          len += strlenW(deformated);
948
949     cmd = msi_alloc(sizeof(WCHAR)*len);
950
951     strcpyW(cmd,prop);
952     if (deformated)
953     {
954         strcatW(cmd,spc);
955         strcatW(cmd,deformated);
956
957         msi_free(deformated);
958     }
959     msi_free(prop);
960
961     TRACE("executing exe %s\n", debugstr_w(cmd));
962
963     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
964                   c_collen, &si, &info);
965
966     if ( !rc )
967     {
968         ERR("Unable to execute command %s\n", debugstr_w(cmd));
969         msi_free(cmd);
970         return ERROR_SUCCESS;
971     }
972     msi_free(cmd);
973
974     CloseHandle( info.hThread );
975
976     return wait_process_handle(package, type, info.hProcess, action);
977 }
978
979 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
980                                 LPCWSTR target, const INT type, LPCWSTR action)
981 {
982     LPWSTR filename, deformated;
983     STARTUPINFOW si;
984     PROCESS_INFORMATION info;
985     BOOL rc;
986
987     memset(&si,0,sizeof(STARTUPINFOW));
988
989     filename = resolve_folder(package, source, FALSE, FALSE, TRUE, NULL);
990
991     if (!filename)
992         return ERROR_FUNCTION_FAILED;
993
994     SetCurrentDirectoryW(filename);
995     msi_free(filename);
996
997     deformat_string(package,target,&deformated);
998
999     if (!deformated)
1000         return ERROR_FUNCTION_FAILED;
1001
1002     TRACE("executing exe %s\n", debugstr_w(deformated));
1003
1004     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
1005                   c_collen, &si, &info);
1006
1007     if ( !rc )
1008     {
1009         ERR("Unable to execute command %s\n", debugstr_w(deformated));
1010         msi_free(deformated);
1011         return ERROR_SUCCESS;
1012     }
1013     msi_free(deformated);
1014     CloseHandle( info.hThread );
1015
1016     return wait_process_handle(package, type, info.hProcess, action);
1017 }
1018
1019 static DWORD WINAPI ACTION_CallScript( const GUID *guid )
1020 {
1021     msi_custom_action_info *info;
1022     MSIHANDLE hPackage;
1023     UINT r = ERROR_FUNCTION_FAILED;
1024
1025     info = find_action_by_guid( guid );
1026     if (!info)
1027     {
1028         ERR("failed to find action %s\n", debugstr_guid( guid) );
1029         return r;
1030     }
1031
1032     TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1033
1034     hPackage = alloc_msihandle( &info->package->hdr );
1035     if (hPackage)
1036     {
1037         r = call_script( hPackage, info->type, info->source, info->target, info->action );
1038         MsiCloseHandle( hPackage );
1039     }
1040     else
1041         ERR("failed to create handle for %p\n", info->package );
1042
1043     if (info->type & msidbCustomActionTypeAsync &&
1044         info->type & msidbCustomActionTypeContinue)
1045         free_custom_action_data( info );
1046
1047     return S_OK;
1048 }
1049
1050 static DWORD WINAPI ScriptThread( LPVOID arg )
1051 {
1052     LPGUID guid = arg;
1053     DWORD rc = 0;
1054
1055     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1056
1057     rc = ACTION_CallScript( guid );
1058
1059     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1060
1061     MsiCloseAllHandles();
1062     return rc;
1063 }
1064
1065 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1066     MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1067 {
1068     msi_custom_action_info *info;
1069
1070     info = msi_alloc( sizeof *info );
1071     if (!info)
1072         return NULL;
1073
1074     msiobj_addref( &package->hdr );
1075     info->package = package;
1076     info->type = type;
1077     info->target = strdupW( function );
1078     info->source = strdupW( script );
1079     info->action = strdupW( action );
1080     CoCreateGuid( &info->guid );
1081
1082     EnterCriticalSection( &msi_custom_action_cs );
1083     list_add_tail( &msi_pending_custom_actions, &info->entry );
1084     LeaveCriticalSection( &msi_custom_action_cs );
1085
1086     info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1087     if (!info->handle)
1088     {
1089         free_custom_action_data( info );
1090         return NULL;
1091     }
1092
1093     return info;
1094 }
1095
1096 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1097                                LPCWSTR target, const INT type, LPCWSTR action)
1098 {
1099     msi_custom_action_info *info;
1100
1101     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1102
1103     info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1104
1105     return wait_thread_handle( info );
1106 }
1107
1108 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1109                                LPCWSTR target, const INT type, LPCWSTR action)
1110 {
1111     static const WCHAR query[] = {
1112         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1113         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1114         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1115     MSIRECORD *row = 0;
1116     msi_custom_action_info *info;
1117     CHAR *buffer = NULL;
1118     WCHAR *bufferw = NULL;
1119     DWORD sz = 0;
1120     UINT r;
1121
1122     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1123
1124     row = MSI_QueryGetRecord(package->db, query, source);
1125     if (!row)
1126         return ERROR_FUNCTION_FAILED;
1127
1128     r = MSI_RecordReadStream(row, 2, NULL, &sz);
1129     if (r != ERROR_SUCCESS)
1130         return r;
1131
1132     buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1133     if (!buffer)
1134         return ERROR_FUNCTION_FAILED;
1135
1136     r = MSI_RecordReadStream(row, 2, buffer, &sz);
1137     if (r != ERROR_SUCCESS)
1138         goto done;
1139
1140     buffer[sz] = 0;
1141     bufferw = strdupAtoW(buffer);
1142     if (!bufferw)
1143     {
1144         r = ERROR_FUNCTION_FAILED;
1145         goto done;
1146     }
1147
1148     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1149     r = wait_thread_handle( info );
1150
1151 done:
1152     msi_free(bufferw);
1153     msi_free(buffer);
1154     return r;
1155 }
1156
1157 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1158                                LPCWSTR target, const INT type, LPCWSTR action)
1159 {
1160     msi_custom_action_info *info;
1161     MSIFILE *file;
1162     HANDLE hFile;
1163     DWORD sz, szHighWord = 0, read;
1164     CHAR *buffer=NULL;
1165     WCHAR *bufferw=NULL;
1166     BOOL bRet;
1167     UINT r;
1168
1169     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1170
1171     file = get_loaded_file(package,source);
1172     if (!file)
1173     {
1174         ERR("invalid file key %s\n", debugstr_w(source));
1175         return ERROR_FUNCTION_FAILED;
1176     }
1177
1178     hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1179     if (hFile == INVALID_HANDLE_VALUE)
1180         return ERROR_FUNCTION_FAILED;
1181
1182     sz = GetFileSize(hFile, &szHighWord);
1183     if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1184     {
1185         CloseHandle(hFile);
1186         return ERROR_FUNCTION_FAILED;
1187     }
1188
1189     buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1190     if (!buffer)
1191     {
1192         CloseHandle(hFile);
1193         return ERROR_FUNCTION_FAILED;
1194     }
1195
1196     bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1197     CloseHandle(hFile);
1198     if (!bRet)
1199     {
1200         r = ERROR_FUNCTION_FAILED;
1201         goto done;
1202     }
1203
1204     buffer[read] = 0;
1205     bufferw = strdupAtoW(buffer);
1206     if (!bufferw)
1207     {
1208         r = ERROR_FUNCTION_FAILED;
1209         goto done;
1210     }
1211
1212     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1213     r = wait_thread_handle( info );
1214
1215 done:
1216     msi_free(bufferw);
1217     msi_free(buffer);
1218     return r;
1219 }
1220
1221 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1222                                LPCWSTR target, const INT type, LPCWSTR action)
1223 {
1224     msi_custom_action_info *info;
1225     WCHAR *prop;
1226
1227     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1228
1229     prop = msi_dup_property(package,source);
1230     if (!prop)
1231         return ERROR_SUCCESS;
1232
1233     info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1234     msi_free(prop);
1235     return wait_thread_handle( info );
1236 }
1237
1238 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1239 {
1240     struct list *item;
1241     HANDLE *wait_handles;
1242     unsigned int handle_count, i;
1243     msi_custom_action_info *info, *cursor;
1244
1245     while ((item = list_head( &package->RunningActions )))
1246     {
1247         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1248
1249         list_remove( &action->entry );
1250
1251         TRACE("waiting for %s\n", debugstr_w( action->name ) );
1252         msi_dialog_check_messages( action->handle );
1253
1254         CloseHandle( action->handle );
1255         msi_free( action->name );
1256         msi_free( action );
1257     }
1258
1259     EnterCriticalSection( &msi_custom_action_cs );
1260
1261     handle_count = list_count( &msi_pending_custom_actions );
1262     wait_handles = HeapAlloc( GetProcessHeap(), 0, handle_count * sizeof(HANDLE) );
1263
1264     handle_count = 0;
1265     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1266     {
1267         if (info->package == package )
1268         {
1269             if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1270                 handle_count++;
1271             free_custom_action_data( info );
1272         }
1273     }
1274
1275     LeaveCriticalSection( &msi_custom_action_cs );
1276
1277     for (i = 0; i < handle_count; i++)
1278     {
1279         msi_dialog_check_messages( wait_handles[i] );
1280         CloseHandle( wait_handles[i] );
1281     }
1282
1283     HeapFree( GetProcessHeap(), 0, wait_handles );
1284 }