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