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