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