msi: Fix the file actions to revert components to the installed state during rollback.
[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 #include "config.h"
22 #include "wine/port.h"
23
24 #define COBJMACROS
25
26 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "msidefs.h"
31 #include "winuser.h"
32 #include "objbase.h"
33 #include "oleauto.h"
34
35 #include "msipriv.h"
36 #include "msiserver.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39 #include "wine/exception.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 #define CUSTOM_ACTION_TYPE_MASK 0x3F
44
45 typedef struct tagMSIRUNNINGACTION
46 {
47     struct list entry;
48     HANDLE handle;
49     BOOL   process;
50     LPWSTR name;
51 } MSIRUNNINGACTION;
52
53 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
54                                LPCWSTR target, const INT type, LPCWSTR action);
55 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
56                                LPCWSTR target, const INT type, LPCWSTR action);
57 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
58                                 LPCWSTR target, const INT type, LPCWSTR action);
59 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
60                                 LPCWSTR target, const INT type, LPCWSTR action);
61 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
62                                 LPCWSTR target, const INT type, LPCWSTR action);
63 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
64                                 LPCWSTR target, const INT type, LPCWSTR action);
65 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
66                                 LPCWSTR target, const INT type, LPCWSTR action);
67 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
68                                 LPCWSTR target, const INT type, LPCWSTR action);
69 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
70                                 LPCWSTR target, const INT type, LPCWSTR action);
71 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
72                                 LPCWSTR target, const INT type, LPCWSTR action);
73 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
74                                 LPCWSTR target, const INT type, LPCWSTR action);
75 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
76                                 LPCWSTR target, const INT type, LPCWSTR action);
77
78 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
79
80 static CRITICAL_SECTION msi_custom_action_cs;
81 static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
82 {
83     0, 0, &msi_custom_action_cs,
84     { &msi_custom_action_cs_debug.ProcessLocksList,
85       &msi_custom_action_cs_debug.ProcessLocksList },
86       0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
87 };
88 static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };
89
90 static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );
91
92 UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action )
93 {
94     UINT count;
95     WCHAR **newbuf = NULL;
96
97     if (script >= TOTAL_SCRIPTS)
98     {
99         FIXME("Unknown script requested %u\n", script);
100         return ERROR_FUNCTION_FAILED;
101     }
102     TRACE("Scheduling action %s in script %u\n", debugstr_w(action), script);
103
104     count = package->script->ActionCount[script];
105     package->script->ActionCount[script]++;
106     if (count != 0) newbuf = msi_realloc( package->script->Actions[script],
107                                           package->script->ActionCount[script] * sizeof(WCHAR *) );
108     else newbuf = msi_alloc( sizeof(WCHAR *) );
109
110     newbuf[count] = strdupW( action );
111     package->script->Actions[script] = newbuf;
112     return ERROR_SUCCESS;
113 }
114
115 UINT msi_register_unique_action( MSIPACKAGE *package, const WCHAR *action )
116 {
117     UINT count;
118     WCHAR **newbuf = NULL;
119
120     if (!package->script) return FALSE;
121
122     TRACE("Registering %s as unique action\n", debugstr_w(action));
123
124     count = package->script->UniqueActionsCount;
125     package->script->UniqueActionsCount++;
126     if (count != 0) newbuf = msi_realloc( package->script->UniqueActions,
127                                           package->script->UniqueActionsCount * sizeof(WCHAR *) );
128     else newbuf = msi_alloc( sizeof(WCHAR *) );
129
130     newbuf[count] = strdupW( action );
131     package->script->UniqueActions = newbuf;
132     return ERROR_SUCCESS;
133 }
134
135 BOOL msi_action_is_unique( const MSIPACKAGE *package, const WCHAR *action )
136 {
137     UINT i;
138
139     if (!package->script) return FALSE;
140
141     for (i = 0; i < package->script->UniqueActionsCount; i++)
142     {
143         if (!strcmpW( package->script->UniqueActions[i], action )) return TRUE;
144     }
145     return FALSE;
146 }
147
148 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
149 {
150     if (!package->script)
151         return TRUE;
152
153     if ((options & msidbCustomActionTypeClientRepeat) ==
154             msidbCustomActionTypeClientRepeat)
155     {
156         if (!(package->script->InWhatSequence & SEQUENCE_UI &&
157             package->script->InWhatSequence & SEQUENCE_EXEC))
158         {
159             TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
160             return FALSE;
161         }
162     }
163     else if (options & msidbCustomActionTypeFirstSequence)
164     {
165         if (package->script->InWhatSequence & SEQUENCE_UI &&
166             package->script->InWhatSequence & SEQUENCE_EXEC )
167         {
168             TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
169             return FALSE;
170         }
171     }
172     else if (options & msidbCustomActionTypeOncePerProcess)
173     {
174         if (msi_action_is_unique(package, action))
175         {
176             TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
177             return FALSE;
178         }
179         else
180             msi_register_unique_action(package, action);
181     }
182
183     return TRUE;
184 }
185
186 /* stores the following properties before the action:
187  *
188  *    [CustomActionData<=>UserSID<=>ProductCode]Action
189  */
190 static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
191                                       LPCWSTR usersid, LPCWSTR prodcode)
192 {
193     LPWSTR deferred;
194     DWORD len;
195
196     static const WCHAR format[] = {
197             '[','%','s','<','=','>','%','s','<','=','>','%','s',']','%','s',0
198     };
199
200     if (!actiondata)
201         return strdupW(action);
202
203     len = lstrlenW(action) + lstrlenW(actiondata) +
204           lstrlenW(usersid) + lstrlenW(prodcode) +
205           lstrlenW(format) - 7;
206     deferred = msi_alloc(len * sizeof(WCHAR));
207
208     sprintfW(deferred, format, actiondata, usersid, prodcode, action);
209     return deferred;
210 }
211
212 static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
213 {
214     LPWSTR end, beg = deferred_data + 1;
215
216     static const WCHAR sep[] = {'<','=','>',0};
217
218     end = strstrW(beg, sep);
219     *end = '\0';
220     msi_set_property(package->db, szCustomActionData, beg);
221     beg = end + 3;
222
223     end = strstrW(beg, sep);
224     *end = '\0';
225     msi_set_property(package->db, szUserSID, beg);
226     beg = end + 3;
227
228     end = strchrW(beg, ']');
229     *end = '\0';
230     msi_set_property(package->db, szProductCode, beg);
231 }
232
233 UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute)
234 {
235     UINT rc = ERROR_SUCCESS;
236     MSIRECORD * row = 0;
237     static const WCHAR ExecSeqQuery[] =
238     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
239      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
240      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
241      '=',' ','\'','%','s','\'',0};
242     UINT type;
243     LPCWSTR source, target;
244     LPWSTR ptr, deferred_data = NULL;
245     LPWSTR action_copy = strdupW(action);
246     WCHAR *deformated=NULL;
247
248     /* deferred action: [properties]Action */
249     if ((ptr = strrchrW(action_copy, ']')))
250     {
251         deferred_data = action_copy;
252         action = ptr + 1;
253     }
254
255     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
256     if (!row)
257     {
258         msi_free(action_copy);
259         return ERROR_CALL_NOT_IMPLEMENTED;
260     }
261
262     type = MSI_RecordGetInteger(row,2);
263
264     source = MSI_RecordGetString(row,3);
265     target = MSI_RecordGetString(row,4);
266
267     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
268           debugstr_w(source), debugstr_w(target));
269
270     /* handle some of the deferred actions */
271     if (type & msidbCustomActionTypeTSAware)
272         FIXME("msidbCustomActionTypeTSAware not handled\n");
273
274     if (type & msidbCustomActionTypeInScript)
275     {
276         if (type & msidbCustomActionTypeNoImpersonate)
277             WARN("msidbCustomActionTypeNoImpersonate not handled\n");
278
279         if (!execute)
280         {
281             LPWSTR actiondata = msi_dup_property(package->db, action);
282             LPWSTR usersid = msi_dup_property(package->db, szUserSID);
283             LPWSTR prodcode = msi_dup_property(package->db, szProductCode);
284             LPWSTR deferred = msi_get_deferred_action(action, actiondata, usersid, prodcode);
285
286             if (type & msidbCustomActionTypeCommit)
287             {
288                 TRACE("Deferring commit action\n");
289                 msi_schedule_action(package, COMMIT_SCRIPT, deferred);
290             }
291             else if (type & msidbCustomActionTypeRollback)
292             {
293                 TRACE("Deferring rollback action\n");
294                 msi_schedule_action(package, ROLLBACK_SCRIPT, deferred);
295             }
296             else
297             {
298                 TRACE("Deferring action\n");
299                 msi_schedule_action(package, INSTALL_SCRIPT, deferred);
300             }
301
302             rc = ERROR_SUCCESS;
303             msi_free(actiondata);
304             msi_free(usersid);
305             msi_free(prodcode);
306             msi_free(deferred);
307             goto end;
308         }
309         else
310         {
311             LPWSTR actiondata = msi_dup_property( package->db, action );
312
313             if (type & msidbCustomActionTypeInScript)
314                 package->scheduled_action_running = TRUE;
315
316             if (type & msidbCustomActionTypeCommit)
317                 package->commit_action_running = TRUE;
318
319             if (type & msidbCustomActionTypeRollback)
320                 package->rollback_action_running = TRUE;
321
322             if (deferred_data)
323                 set_deferred_action_props(package, deferred_data);
324             else if (actiondata)
325                 msi_set_property(package->db, szCustomActionData, actiondata);
326             else
327                 msi_set_property(package->db, szCustomActionData, szEmpty);
328
329             msi_free(actiondata);
330         }
331     }
332     else if (!check_execution_scheduling_options(package,action,type))
333     {
334         rc = ERROR_SUCCESS;
335         goto end;
336     }
337
338     switch (type & CUSTOM_ACTION_TYPE_MASK)
339     {
340         case 1: /* DLL file stored in a Binary table stream */
341             rc = HANDLE_CustomType1(package,source,target,type,action);
342             break;
343         case 2: /* EXE file stored in a Binary table stream */
344             rc = HANDLE_CustomType2(package,source,target,type,action);
345             break;
346         case 18: /*EXE file installed with package */
347             rc = HANDLE_CustomType18(package,source,target,type,action);
348             break;
349         case 19: /* Error that halts install */
350             rc = HANDLE_CustomType19(package,source,target,type,action);
351             break;
352         case 17:
353             rc = HANDLE_CustomType17(package,source,target,type,action);
354             break;
355         case 23: /* installs another package in the source tree */
356             deformat_string(package,target,&deformated);
357             rc = HANDLE_CustomType23(package,source,deformated,type,action);
358             msi_free(deformated);
359             break;
360         case 50: /*EXE file specified by a property value */
361             rc = HANDLE_CustomType50(package,source,target,type,action);
362             break;
363         case 34: /*EXE to be run in specified directory */
364             rc = HANDLE_CustomType34(package,source,target,type,action);
365             break;
366         case 35: /* Directory set with formatted text. */
367             deformat_string(package,target,&deformated);
368             MSI_SetTargetPathW(package, source, deformated);
369             msi_free(deformated);
370             break;
371         case 51: /* Property set with formatted text. */
372             if (!source)
373                 break;
374
375             deformat_string(package,target,&deformated);
376             rc = msi_set_property( package->db, source, deformated );
377             if (rc == ERROR_SUCCESS && !strcmpW( source, szSourceDir ))
378                 msi_reset_folders( package, TRUE );
379             msi_free(deformated);
380             break;
381         case 37: /* JScript/VBScript text stored in target column. */
382         case 38:
383             rc = HANDLE_CustomType37_38(package,source,target,type,action);
384             break;
385         case 5:
386         case 6: /* JScript/VBScript file stored in a Binary table stream. */
387             rc = HANDLE_CustomType5_6(package,source,target,type,action);
388             break;
389         case 21: /* JScript/VBScript file installed with the product. */
390         case 22:
391             rc = HANDLE_CustomType21_22(package,source,target,type,action);
392             break;
393         case 53: /* JScript/VBScript text specified by a property value. */
394         case 54:
395             rc = HANDLE_CustomType53_54(package,source,target,type,action);
396             break;
397         default:
398             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
399              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
400              debugstr_w(target));
401     }
402
403 end:
404     package->scheduled_action_running = FALSE;
405     package->commit_action_running = FALSE;
406     package->rollback_action_running = FALSE;
407     msi_free(action_copy);
408     msiobj_release(&row->hdr);
409     return rc;
410 }
411
412 static MSIBINARY *create_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
413 {
414     static const WCHAR query[] = {
415         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
416         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
417         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
418     MSIRECORD *row;
419     MSIBINARY *binary;
420     HANDLE file;
421     CHAR buffer[1024];
422     WCHAR fmt[MAX_PATH], tmpfile[MAX_PATH];
423     DWORD sz = MAX_PATH, write;
424     UINT r;
425
426     if (msi_get_property(package->db, szTempFolder, fmt, &sz) != ERROR_SUCCESS)
427         GetTempPathW(MAX_PATH, fmt);
428
429     if (!GetTempFileNameW( fmt, szMsi, 0, tmpfile ))
430     {
431         TRACE("unable to create temp file %s (%u)\n", debugstr_w(tmpfile), GetLastError());
432         return NULL;
433     }
434
435     row = MSI_QueryGetRecord(package->db, query, source);
436     if (!row)
437         return NULL;
438
439     if (!(binary = msi_alloc_zero( sizeof(MSIBINARY) )))
440     {
441         msiobj_release( &row->hdr );
442         return NULL;
443     }
444     file = CreateFileW( tmpfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
445     if (file == INVALID_HANDLE_VALUE)
446     {
447         msiobj_release( &row->hdr );
448         msi_free( binary );
449         return NULL;
450     }
451     do
452     {
453         sz = sizeof(buffer);
454         r = MSI_RecordReadStream( row, 2, buffer, &sz );
455         if (r != ERROR_SUCCESS)
456         {
457             ERR("Failed to get stream\n");
458             break;
459         }
460         WriteFile( file, buffer, sz, &write, NULL );
461     } while (sz == sizeof buffer);
462
463     CloseHandle( file );
464     msiobj_release( &row->hdr );
465     if (r != ERROR_SUCCESS)
466     {
467         DeleteFileW( tmpfile );
468         msi_free( binary );
469         return NULL;
470     }
471
472     /* keep a reference to prevent the dll from being unloaded */
473     if (dll && !(binary->module = LoadLibraryW( tmpfile )))
474     {
475         WARN( "failed to load dll %s (%u)\n", debugstr_w( tmpfile ), GetLastError() );
476     }
477     binary->source = strdupW( source );
478     binary->tmpfile = strdupW( tmpfile );
479     list_add_tail( &package->binaries, &binary->entry );
480     return binary;
481 }
482
483 static MSIBINARY *get_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
484 {
485     MSIBINARY *binary;
486
487     LIST_FOR_EACH_ENTRY( binary, &package->binaries, MSIBINARY, entry )
488     {
489         if (!strcmpW( binary->source, source ))
490             return binary;
491     }
492
493     return create_temp_binary( package, source, dll );
494 }
495
496 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
497                                 BOOL process, LPCWSTR name)
498 {
499     MSIRUNNINGACTION *action;
500
501     action = msi_alloc( sizeof(MSIRUNNINGACTION) );
502
503     action->handle = Handle;
504     action->process = process;
505     action->name = strdupW(name);
506
507     list_add_tail( &package->RunningActions, &action->entry );
508 }
509
510 static UINT custom_get_process_return( HANDLE process )
511 {
512     DWORD rc = 0;
513
514     GetExitCodeProcess( process, &rc );
515     if (rc != 0)
516         return ERROR_FUNCTION_FAILED;
517     return ERROR_SUCCESS;
518 }
519
520 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
521 {
522     DWORD rc = 0;
523
524     GetExitCodeThread( thread, &rc );
525
526     switch (rc)
527     {
528     case ERROR_FUNCTION_NOT_CALLED:
529     case ERROR_SUCCESS:
530     case ERROR_INSTALL_USEREXIT:
531     case ERROR_INSTALL_FAILURE:
532         return rc;
533     case ERROR_NO_MORE_ITEMS:
534         return ERROR_SUCCESS;
535     case ERROR_INSTALL_SUSPEND:
536         ACTION_ForceReboot( package );
537         return ERROR_SUCCESS;
538     default:
539         ERR("Invalid Return Code %d\n",rc);
540         return ERROR_INSTALL_FAILURE;
541     }
542 }
543
544 static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
545                            HANDLE ProcessHandle, LPCWSTR name)
546 {
547     UINT rc = ERROR_SUCCESS;
548
549     if (!(type & msidbCustomActionTypeAsync))
550     {
551         TRACE("waiting for %s\n", debugstr_w(name));
552
553         msi_dialog_check_messages(ProcessHandle);
554
555         if (!(type & msidbCustomActionTypeContinue))
556             rc = custom_get_process_return(ProcessHandle);
557
558         CloseHandle(ProcessHandle);
559     }
560     else
561     {
562         TRACE("%s running in background\n", debugstr_w(name));
563
564         if (!(type & msidbCustomActionTypeContinue))
565             file_running_action(package, ProcessHandle, TRUE, name);
566         else
567             CloseHandle(ProcessHandle);
568     }
569
570     return rc;
571 }
572
573 typedef struct _msi_custom_action_info {
574     struct list entry;
575     LONG refs;
576     MSIPACKAGE *package;
577     LPWSTR source;
578     LPWSTR target;
579     HANDLE handle;
580     LPWSTR action;
581     INT type;
582     GUID guid;
583 } msi_custom_action_info;
584
585 static void release_custom_action_data( msi_custom_action_info *info )
586 {
587     EnterCriticalSection( &msi_custom_action_cs );
588
589     if (!--info->refs)
590     {
591         list_remove( &info->entry );
592         if (info->handle)
593             CloseHandle( info->handle );
594         msi_free( info->action );
595         msi_free( info->source );
596         msi_free( info->target );
597         msiobj_release( &info->package->hdr );
598         msi_free( info );
599     }
600
601     LeaveCriticalSection( &msi_custom_action_cs );
602 }
603
604 /* must be called inside msi_custom_action_cs if info is in the pending custom actions list */
605 static void addref_custom_action_data( msi_custom_action_info *info )
606 {
607     info->refs++;
608  }
609
610 static UINT wait_thread_handle( msi_custom_action_info *info )
611 {
612     UINT rc = ERROR_SUCCESS;
613
614     if (!(info->type & msidbCustomActionTypeAsync))
615     {
616         TRACE("waiting for %s\n", debugstr_w( info->action ));
617
618         msi_dialog_check_messages( info->handle );
619
620         if (!(info->type & msidbCustomActionTypeContinue))
621             rc = custom_get_thread_return( info->package, info->handle );
622
623         release_custom_action_data( info );
624     }
625     else
626     {
627         TRACE("%s running in background\n", debugstr_w( info->action ));
628     }
629
630     return rc;
631 }
632
633 static msi_custom_action_info *find_action_by_guid( const GUID *guid )
634 {
635     msi_custom_action_info *info;
636     BOOL found = FALSE;
637
638     EnterCriticalSection( &msi_custom_action_cs );
639
640     LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
641     {
642         if (IsEqualGUID( &info->guid, guid ))
643         {
644             addref_custom_action_data( info );
645             found = TRUE;
646             break;
647         }
648     }
649
650     LeaveCriticalSection( &msi_custom_action_cs );
651
652     if (!found)
653         return NULL;
654
655     return info;
656 }
657
658 static void handle_msi_break( LPCWSTR target )
659 {
660     LPWSTR msg;
661     WCHAR val[MAX_PATH];
662
663     static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
664     static const WCHAR WindowsInstaller[] = {
665         'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
666     };
667
668     static const WCHAR format[] = {
669         'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
670         'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
671         'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
672         't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
673         'a','n','d',' ','p','r','e','s','s',' ','O','K',0
674     };
675
676     if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
677         return;
678
679     if( strcmpiW( val, target ))
680         return;
681
682     msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
683     if (!msg)
684         return;
685
686     wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
687     MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
688     msi_free(msg);
689     DebugBreak();
690 }
691
692 static UINT get_action_info( const GUID *guid, INT *type, MSIHANDLE *handle,
693                              BSTR *dll, BSTR *funcname,
694                              IWineMsiRemotePackage **package )
695 {
696     IClassFactory *cf = NULL;
697     IWineMsiRemoteCustomAction *rca = NULL;
698     HRESULT r;
699
700     r = DllGetClassObject( &CLSID_WineMsiRemoteCustomAction,
701                            &IID_IClassFactory, (LPVOID *)&cf );
702     if (FAILED(r))
703     {
704         ERR("failed to get IClassFactory interface\n");
705         return ERROR_FUNCTION_FAILED;
706     }
707
708     r = IClassFactory_CreateInstance( cf, NULL, &IID_IWineMsiRemoteCustomAction, (LPVOID *)&rca );
709     if (FAILED(r))
710     {
711         ERR("failed to get IWineMsiRemoteCustomAction interface\n");
712         return ERROR_FUNCTION_FAILED;
713     }
714
715     r = IWineMsiRemoteCustomAction_GetActionInfo( rca, guid, type, handle, dll, funcname, package );
716     IWineMsiRemoteCustomAction_Release( rca );
717     if (FAILED(r))
718     {
719         ERR("GetActionInfo failed\n");
720         return ERROR_FUNCTION_FAILED;
721     }
722
723     return ERROR_SUCCESS;
724 }
725
726 #ifdef __i386__
727 extern UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle );
728 __ASM_GLOBAL_FUNC( CUSTOMPROC_wrapper,
729         "pushl %ebp\n\t"
730         __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
731         __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
732         "movl %esp,%ebp\n\t"
733         __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
734         "pushl 12(%ebp)\n\t"
735         "movl 8(%ebp),%eax\n\t"
736         "call *%eax\n\t"
737         "leave\n\t"
738         __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
739         __ASM_CFI(".cfi_same_value %ebp\n\t")
740         "ret" )
741 #else
742 static inline UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle )
743 {
744         return proc(handle);
745 }
746 #endif
747
748 static DWORD ACTION_CallDllFunction( const GUID *guid )
749 {
750     MsiCustomActionEntryPoint fn;
751     MSIHANDLE hPackage, handle;
752     HANDLE hModule;
753     LPSTR proc;
754     UINT r = ERROR_FUNCTION_FAILED;
755     BSTR dll = NULL, function = NULL;
756     INT type;
757     IWineMsiRemotePackage *remote_package = NULL;
758
759     TRACE("%s\n", debugstr_guid( guid ));
760
761     r = get_action_info( guid, &type, &handle, &dll, &function, &remote_package );
762     if (r != ERROR_SUCCESS)
763         return r;
764
765     hModule = LoadLibraryW( dll );
766     if (!hModule)
767     {
768         WARN( "failed to load dll %s (%u)\n", debugstr_w( dll ), GetLastError() );
769         return ERROR_SUCCESS;
770     }
771
772     proc = strdupWtoA( function );
773     fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
774     msi_free( proc );
775     if (fn)
776     {
777         hPackage = alloc_msi_remote_handle( (IUnknown *)remote_package );
778         if (hPackage)
779         {
780             IWineMsiRemotePackage_SetMsiHandle( remote_package, handle );
781             TRACE("calling %s\n", debugstr_w( function ) );
782             handle_msi_break( function );
783
784             __TRY
785             {
786                 r = CUSTOMPROC_wrapper( fn, hPackage );
787             }
788             __EXCEPT_PAGE_FAULT
789             {
790                 ERR("Custom action (%s:%s) caused a page fault: %08x\n",
791                     debugstr_w(dll), debugstr_w(function), GetExceptionCode());
792                 r = ERROR_SUCCESS;
793             }
794             __ENDTRY;
795
796             MsiCloseHandle( hPackage );
797         }
798         else
799             ERR("failed to create handle for %p\n", remote_package );
800     }
801     else
802         ERR("GetProcAddress(%s) failed\n", debugstr_w( function ) );
803
804     FreeLibrary(hModule);
805
806     IWineMsiRemotePackage_Release( remote_package );
807     SysFreeString( dll );
808     SysFreeString( function );
809     MsiCloseHandle( handle );
810
811     return r;
812 }
813
814 static DWORD WINAPI DllThread( LPVOID arg )
815 {
816     LPGUID guid = arg;
817     DWORD rc = 0;
818
819     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
820
821     rc = ACTION_CallDllFunction( guid );
822
823     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
824
825     MsiCloseAllHandles();
826     return rc;
827 }
828
829 static DWORD ACTION_CAInstallPackage(const GUID *guid)
830 {
831     msi_custom_action_info *info;
832     UINT r = ERROR_FUNCTION_FAILED;
833     INSTALLUILEVEL old_level;
834
835     info = find_action_by_guid(guid);
836     if (!info)
837     {
838         ERR("failed to find action %s\n", debugstr_guid(guid));
839         return r;
840     }
841
842     old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
843     r = MsiInstallProductW(info->source, info->target);
844     MsiSetInternalUI(old_level, NULL);
845
846     release_custom_action_data(info);
847
848     return r;
849 }
850
851 static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
852 {
853     LPGUID guid = arg;
854     DWORD rc;
855
856     TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());
857
858     rc = ACTION_CAInstallPackage(guid);
859
860     TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);
861
862     MsiCloseAllHandles();
863     return rc;
864 }
865
866 static msi_custom_action_info *do_msidbCustomActionTypeDll(
867     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
868 {
869     msi_custom_action_info *info;
870
871     info = msi_alloc( sizeof *info );
872     if (!info)
873         return NULL;
874
875     msiobj_addref( &package->hdr );
876     info->refs = 2; /* 1 for our caller and 1 for thread we created */
877     info->package = package;
878     info->type = type;
879     info->target = strdupW( target );
880     info->source = strdupW( source );
881     info->action = strdupW( action );
882     CoCreateGuid( &info->guid );
883
884     EnterCriticalSection( &msi_custom_action_cs );
885     list_add_tail( &msi_pending_custom_actions, &info->entry );
886     LeaveCriticalSection( &msi_custom_action_cs );
887
888     info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
889     if (!info->handle)
890     {
891         /* release both references */
892         release_custom_action_data( info );
893         release_custom_action_data( info );
894         return NULL;
895     }
896
897     return info;
898 }
899
900 static msi_custom_action_info *do_msidbCAConcurrentInstall(
901     MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
902 {
903     msi_custom_action_info *info;
904
905     info = msi_alloc( sizeof *info );
906     if (!info)
907         return NULL;
908
909     msiobj_addref( &package->hdr );
910     info->refs = 2; /* 1 for our caller and 1 for thread we created */
911     info->package = package;
912     info->type = type;
913     info->target = strdupW( target );
914     info->source = strdupW( source );
915     info->action = strdupW( action );
916     CoCreateGuid( &info->guid );
917
918     EnterCriticalSection( &msi_custom_action_cs );
919     list_add_tail( &msi_pending_custom_actions, &info->entry );
920     LeaveCriticalSection( &msi_custom_action_cs );
921
922     info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
923     if (!info->handle)
924     {
925         /* release both references */
926         release_custom_action_data( info );
927         release_custom_action_data( info );
928         return NULL;
929     }
930
931     return info;
932 }
933
934 static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
935                                 LPCWSTR target, const INT type, LPCWSTR action)
936 {
937     msi_custom_action_info *info;
938     WCHAR package_path[MAX_PATH];
939     DWORD size;
940     UINT r;
941
942     size = MAX_PATH;
943     msi_get_property(package->db, szSourceDir, package_path, &size);
944     lstrcatW(package_path, szBackSlash);
945     lstrcatW(package_path, source);
946
947     TRACE("Installing package %s concurrently\n", debugstr_w(package_path));
948
949     info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
950
951     r = wait_thread_handle(info);
952     release_custom_action_data( info );
953     return r;
954 }
955
956 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
957                                LPCWSTR target, const INT type, LPCWSTR action)
958 {
959     msi_custom_action_info *info;
960     MSIBINARY *binary;
961     UINT r;
962
963     if (!(binary = get_temp_binary( package, source, TRUE )))
964         return ERROR_FUNCTION_FAILED;
965
966     TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile));
967
968     info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action );
969
970     r = wait_thread_handle( info );
971     release_custom_action_data( info );
972     return r;
973 }
974
975 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
976                                LPCWSTR target, const INT type, LPCWSTR action)
977 {
978     STARTUPINFOW si;
979     PROCESS_INFORMATION info;
980     BOOL rc;
981     INT len;
982     WCHAR *deformated = NULL;
983     WCHAR *cmd;
984     static const WCHAR spc[] = {' ',0};
985     MSIBINARY *binary;
986     UINT r;
987
988     memset(&si,0,sizeof(STARTUPINFOW));
989
990     if (!(binary = get_temp_binary( package, source, FALSE )))
991         return ERROR_FUNCTION_FAILED;
992
993     deformat_string(package,target,&deformated);
994
995     len = strlenW( binary->tmpfile ) + 2;
996     if (deformated)
997         len += strlenW(deformated);
998
999     cmd = msi_alloc(sizeof(WCHAR)*len);
1000
1001     strcpyW( cmd, binary->tmpfile );
1002     if (deformated)
1003     {
1004         strcatW(cmd,spc);
1005         strcatW(cmd,deformated);
1006         msi_free(deformated);
1007     }
1008
1009     TRACE("executing exe %s\n", debugstr_w(cmd));
1010
1011     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, szCRoot, &si, &info);
1012     msi_free(cmd);
1013
1014     if ( !rc )
1015     {
1016         ERR("Unable to execute command %s\n", debugstr_w(cmd));
1017         return ERROR_SUCCESS;
1018     }
1019     CloseHandle( info.hThread );
1020
1021     r = wait_process_handle(package, type, info.hProcess, action);
1022
1023     return r;
1024 }
1025
1026 static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
1027                                 LPCWSTR target, const INT type, LPCWSTR action)
1028 {
1029     msi_custom_action_info *info;
1030     MSIFILE *file;
1031     UINT r;
1032
1033     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1034
1035     file = msi_get_loaded_file( package, source );
1036     if (!file)
1037     {
1038         ERR("invalid file key %s\n", debugstr_w( source ));
1039         return ERROR_FUNCTION_FAILED;
1040     }
1041
1042     info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
1043
1044     r = wait_thread_handle( info );
1045     release_custom_action_data( info );
1046     return r;
1047 }
1048
1049 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
1050                                 LPCWSTR target, const INT type, LPCWSTR action)
1051 {
1052     STARTUPINFOW si;
1053     PROCESS_INFORMATION info;
1054     BOOL rc;
1055     WCHAR *deformated;
1056     WCHAR *cmd;
1057     INT len;
1058     static const WCHAR spc[] = {' ',0};
1059     MSIFILE *file;
1060
1061     memset(&si,0,sizeof(STARTUPINFOW));
1062
1063     file = msi_get_loaded_file(package, source);
1064     if( !file )
1065         return ERROR_FUNCTION_FAILED;
1066
1067     len = lstrlenW( file->TargetPath );
1068
1069     deformat_string(package,target,&deformated);
1070     if (deformated)
1071         len += strlenW(deformated);
1072     len += 2;
1073
1074     cmd = msi_alloc(len * sizeof(WCHAR));
1075
1076     lstrcpyW( cmd, file->TargetPath);
1077     if (deformated)
1078     {
1079         strcatW(cmd, spc);
1080         strcatW(cmd, deformated);
1081
1082         msi_free(deformated);
1083     }
1084
1085     TRACE("executing exe %s\n", debugstr_w(cmd));
1086
1087     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, szCRoot, &si, &info);
1088
1089     if ( !rc )
1090     {
1091         ERR("Unable to execute command %s\n", debugstr_w(cmd));
1092         msi_free(cmd);
1093         return ERROR_SUCCESS;
1094     }
1095     msi_free(cmd);
1096     CloseHandle( info.hThread );
1097
1098     return wait_process_handle(package, type, info.hProcess, action);
1099 }
1100
1101 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
1102                                 LPCWSTR target, const INT type, LPCWSTR action)
1103 {
1104     static const WCHAR query[] = {
1105       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
1106       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
1107       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
1108       '%','s',0
1109     };
1110     MSIRECORD *row = 0;
1111     LPWSTR deformated = NULL;
1112
1113     deformat_string( package, target, &deformated );
1114
1115     /* first try treat the error as a number */
1116     row = MSI_QueryGetRecord( package->db, query, deformated );
1117     if( row )
1118     {
1119         LPCWSTR error = MSI_RecordGetString( row, 1 );
1120         if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
1121             MessageBoxW( NULL, error, NULL, MB_OK );
1122         msiobj_release( &row->hdr );
1123     }
1124     else if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
1125         MessageBoxW( NULL, deformated, NULL, MB_OK );
1126
1127     msi_free( deformated );
1128
1129     return ERROR_INSTALL_FAILURE;
1130 }
1131
1132 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
1133                                 LPCWSTR target, const INT type, LPCWSTR action)
1134 {
1135     STARTUPINFOW si;
1136     PROCESS_INFORMATION info;
1137     WCHAR *prop;
1138     BOOL rc;
1139     WCHAR *deformated;
1140     WCHAR *cmd;
1141     INT len;
1142     static const WCHAR spc[] = {' ',0};
1143
1144     memset(&si,0,sizeof(STARTUPINFOW));
1145     memset(&info,0,sizeof(PROCESS_INFORMATION));
1146
1147     prop = msi_dup_property( package->db, source );
1148     if (!prop)
1149         return ERROR_SUCCESS;
1150
1151     deformat_string(package,target,&deformated);
1152     len = strlenW(prop) + 2;
1153     if (deformated)
1154          len += strlenW(deformated);
1155
1156     cmd = msi_alloc(sizeof(WCHAR)*len);
1157
1158     strcpyW(cmd,prop);
1159     if (deformated)
1160     {
1161         strcatW(cmd,spc);
1162         strcatW(cmd,deformated);
1163
1164         msi_free(deformated);
1165     }
1166     msi_free(prop);
1167
1168     TRACE("executing exe %s\n", debugstr_w(cmd));
1169
1170     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, szCRoot, &si, &info);
1171
1172     if ( !rc )
1173     {
1174         ERR("Unable to execute command %s\n", debugstr_w(cmd));
1175         msi_free(cmd);
1176         return ERROR_SUCCESS;
1177     }
1178     msi_free(cmd);
1179
1180     CloseHandle( info.hThread );
1181
1182     return wait_process_handle(package, type, info.hProcess, action);
1183 }
1184
1185 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
1186                                 LPCWSTR target, const INT type, LPCWSTR action)
1187 {
1188     LPWSTR filename;
1189     const WCHAR *workingdir;
1190     STARTUPINFOW si;
1191     PROCESS_INFORMATION info;
1192     BOOL rc;
1193
1194     memset(&si, 0, sizeof(STARTUPINFOW));
1195
1196     workingdir = msi_get_target_folder( package, source );
1197     if (!workingdir) return ERROR_FUNCTION_FAILED;
1198
1199     deformat_string(package, target, &filename);
1200     if (!filename) return ERROR_FUNCTION_FAILED;
1201
1202     TRACE("executing exe %s with working directory %s\n",
1203           debugstr_w(filename), debugstr_w(workingdir));
1204
1205     rc = CreateProcessW(NULL, filename, NULL, NULL, FALSE, 0, NULL,
1206                         workingdir, &si, &info);
1207
1208     if ( !rc )
1209     {
1210         ERR("Unable to execute command %s with working directory %s\n",
1211             debugstr_w(filename), debugstr_w(workingdir));
1212         msi_free(filename);
1213         return ERROR_SUCCESS;
1214     }
1215     msi_free(filename);
1216     CloseHandle( info.hThread );
1217
1218     return wait_process_handle(package, type, info.hProcess, action);
1219 }
1220
1221 static DWORD ACTION_CallScript( const GUID *guid )
1222 {
1223     msi_custom_action_info *info;
1224     MSIHANDLE hPackage;
1225     UINT r;
1226
1227     info = find_action_by_guid( guid );
1228     if (!info)
1229     {
1230         ERR("failed to find action %s\n", debugstr_guid( guid) );
1231         return ERROR_FUNCTION_FAILED;
1232     }
1233
1234     TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );
1235
1236     hPackage = alloc_msihandle( &info->package->hdr );
1237     if (hPackage)
1238     {
1239         r = call_script( hPackage, info->type, info->source, info->target, info->action );
1240         TRACE("script returned %u\n", r);
1241         MsiCloseHandle( hPackage );
1242     }
1243     else
1244         ERR("failed to create handle for %p\n", info->package );
1245
1246     release_custom_action_data( info );
1247     return S_OK;
1248 }
1249
1250 static DWORD WINAPI ScriptThread( LPVOID arg )
1251 {
1252     LPGUID guid = arg;
1253     DWORD rc = 0;
1254
1255     TRACE("custom action (%x) started\n", GetCurrentThreadId() );
1256
1257     rc = ACTION_CallScript( guid );
1258
1259     TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
1260
1261     MsiCloseAllHandles();
1262     return rc;
1263 }
1264
1265 static msi_custom_action_info *do_msidbCustomActionTypeScript(
1266     MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
1267 {
1268     msi_custom_action_info *info;
1269
1270     info = msi_alloc( sizeof *info );
1271     if (!info)
1272         return NULL;
1273
1274     msiobj_addref( &package->hdr );
1275     info->refs = 2; /* 1 for our caller and 1 for thread we created */
1276     info->package = package;
1277     info->type = type;
1278     info->target = strdupW( function );
1279     info->source = strdupW( script );
1280     info->action = strdupW( action );
1281     CoCreateGuid( &info->guid );
1282
1283     EnterCriticalSection( &msi_custom_action_cs );
1284     list_add_tail( &msi_pending_custom_actions, &info->entry );
1285     LeaveCriticalSection( &msi_custom_action_cs );
1286
1287     info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
1288     if (!info->handle)
1289     {
1290         /* release both references */
1291         release_custom_action_data( info );
1292         release_custom_action_data( info );
1293         return NULL;
1294     }
1295
1296     return info;
1297 }
1298
1299 static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
1300                                LPCWSTR target, const INT type, LPCWSTR action)
1301 {
1302     UINT r;
1303     msi_custom_action_info *info;
1304
1305     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1306
1307     info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1308
1309     r = wait_thread_handle( info );
1310     release_custom_action_data( info );
1311     return r;
1312 }
1313
1314 static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
1315                                LPCWSTR target, const INT type, LPCWSTR action)
1316 {
1317     static const WCHAR query[] = {
1318         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1319         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
1320         '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
1321     MSIRECORD *row = 0;
1322     msi_custom_action_info *info;
1323     CHAR *buffer = NULL;
1324     WCHAR *bufferw = NULL;
1325     DWORD sz = 0;
1326     UINT r;
1327
1328     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1329
1330     row = MSI_QueryGetRecord(package->db, query, source);
1331     if (!row)
1332         return ERROR_FUNCTION_FAILED;
1333
1334     r = MSI_RecordReadStream(row, 2, NULL, &sz);
1335     if (r != ERROR_SUCCESS)
1336         return r;
1337
1338     buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1339     if (!buffer)
1340         return ERROR_FUNCTION_FAILED;
1341
1342     r = MSI_RecordReadStream(row, 2, buffer, &sz);
1343     if (r != ERROR_SUCCESS)
1344         goto done;
1345
1346     buffer[sz] = 0;
1347     bufferw = strdupAtoW(buffer);
1348     if (!bufferw)
1349     {
1350         r = ERROR_FUNCTION_FAILED;
1351         goto done;
1352     }
1353
1354     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1355     r = wait_thread_handle( info );
1356     release_custom_action_data( info );
1357
1358 done:
1359     msi_free(bufferw);
1360     msi_free(buffer);
1361     return r;
1362 }
1363
1364 static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
1365                                LPCWSTR target, const INT type, LPCWSTR action)
1366 {
1367     msi_custom_action_info *info;
1368     MSIFILE *file;
1369     HANDLE hFile;
1370     DWORD sz, szHighWord = 0, read;
1371     CHAR *buffer=NULL;
1372     WCHAR *bufferw=NULL;
1373     BOOL bRet;
1374     UINT r;
1375
1376     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1377
1378     file = msi_get_loaded_file(package, source);
1379     if (!file)
1380     {
1381         ERR("invalid file key %s\n", debugstr_w(source));
1382         return ERROR_FUNCTION_FAILED;
1383     }
1384
1385     hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1386     if (hFile == INVALID_HANDLE_VALUE)
1387         return ERROR_FUNCTION_FAILED;
1388
1389     sz = GetFileSize(hFile, &szHighWord);
1390     if (sz == INVALID_FILE_SIZE || szHighWord != 0)
1391     {
1392         CloseHandle(hFile);
1393         return ERROR_FUNCTION_FAILED;
1394     }
1395
1396     buffer = msi_alloc(sizeof(CHAR)*(sz+1));
1397     if (!buffer)
1398     {
1399         CloseHandle(hFile);
1400         return ERROR_FUNCTION_FAILED;
1401     }
1402
1403     bRet = ReadFile(hFile, buffer, sz, &read, NULL);
1404     CloseHandle(hFile);
1405     if (!bRet)
1406     {
1407         r = ERROR_FUNCTION_FAILED;
1408         goto done;
1409     }
1410
1411     buffer[read] = 0;
1412     bufferw = strdupAtoW(buffer);
1413     if (!bufferw)
1414     {
1415         r = ERROR_FUNCTION_FAILED;
1416         goto done;
1417     }
1418
1419     info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
1420     r = wait_thread_handle( info );
1421     release_custom_action_data( info );
1422
1423 done:
1424     msi_free(bufferw);
1425     msi_free(buffer);
1426     return r;
1427 }
1428
1429 static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
1430                                LPCWSTR target, const INT type, LPCWSTR action)
1431 {
1432     msi_custom_action_info *info;
1433     WCHAR *prop;
1434     UINT r;
1435
1436     TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));
1437
1438     prop = msi_dup_property( package->db, source );
1439     if (!prop)
1440         return ERROR_SUCCESS;
1441
1442     info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
1443     msi_free(prop);
1444     r = wait_thread_handle( info );
1445     release_custom_action_data( info );
1446     return r;
1447 }
1448
1449 void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1450 {
1451     struct list *item;
1452     HANDLE *wait_handles;
1453     unsigned int handle_count, i;
1454     msi_custom_action_info *info, *cursor;
1455
1456     while ((item = list_head( &package->RunningActions )))
1457     {
1458         MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
1459
1460         list_remove( &action->entry );
1461
1462         TRACE("waiting for %s\n", debugstr_w( action->name ) );
1463         msi_dialog_check_messages( action->handle );
1464
1465         CloseHandle( action->handle );
1466         msi_free( action->name );
1467         msi_free( action );
1468     }
1469
1470     EnterCriticalSection( &msi_custom_action_cs );
1471
1472     handle_count = list_count( &msi_pending_custom_actions );
1473     wait_handles = msi_alloc( handle_count * sizeof(HANDLE) );
1474
1475     handle_count = 0;
1476     LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
1477     {
1478         if (info->package == package )
1479         {
1480             if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
1481                 handle_count++;
1482         }
1483     }
1484
1485     LeaveCriticalSection( &msi_custom_action_cs );
1486
1487     for (i = 0; i < handle_count; i++)
1488     {
1489         msi_dialog_check_messages( wait_handles[i] );
1490         CloseHandle( wait_handles[i] );
1491     }
1492
1493     msi_free( wait_handles );
1494 }
1495
1496 typedef struct _msi_custom_remote_impl {
1497     IWineMsiRemoteCustomAction IWineMsiRemoteCustomAction_iface;
1498     LONG refs;
1499 } msi_custom_remote_impl;
1500
1501 static inline msi_custom_remote_impl *impl_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction *iface )
1502 {
1503     return CONTAINING_RECORD(iface, msi_custom_remote_impl, IWineMsiRemoteCustomAction_iface);
1504 }
1505
1506 static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface,
1507                 REFIID riid,LPVOID *ppobj)
1508 {
1509     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
1510         IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) )
1511     {
1512         IUnknown_AddRef( iface );
1513         *ppobj = iface;
1514         return S_OK;
1515     }
1516
1517     return E_NOINTERFACE;
1518 }
1519
1520 static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface )
1521 {
1522     msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1523
1524     return InterlockedIncrement( &This->refs );
1525 }
1526
1527 static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface )
1528 {
1529     msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1530     ULONG r;
1531
1532     r = InterlockedDecrement( &This->refs );
1533     if (r == 0)
1534         msi_free( This );
1535     return r;
1536 }
1537
1538 static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid,
1539          INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package )
1540 {
1541     msi_custom_action_info *info;
1542
1543     info = find_action_by_guid( custom_action_guid );
1544     if (!info)
1545         return E_FAIL;
1546
1547     *type = info->type;
1548     *handle = alloc_msihandle( &info->package->hdr );
1549     *dll = SysAllocString( info->source );
1550     *func = SysAllocString( info->target );
1551
1552     release_custom_action_data( info );
1553     return create_msi_remote_package( NULL, (LPVOID *)remote_package );
1554 }
1555
1556 static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl =
1557 {
1558     mcr_QueryInterface,
1559     mcr_AddRef,
1560     mcr_Release,
1561     mcr_GetActionInfo,
1562 };
1563
1564 HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
1565 {
1566     msi_custom_remote_impl* This;
1567
1568     This = msi_alloc( sizeof *This );
1569     if (!This)
1570         return E_OUTOFMEMORY;
1571
1572     This->IWineMsiRemoteCustomAction_iface.lpVtbl = &msi_custom_remote_vtbl;
1573     This->refs = 1;
1574
1575     *ppObj = This;
1576
1577     return S_OK;
1578 }