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