Don't open device if already open.
[wine] / dlls / msi / events.c
1 /*
2  * Implementation of 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21
22 /*
23 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/controlevent_overview.asp
24 */
25
26 #include <stdarg.h>
27 #include <stdio.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winerror.h"
32 #include "winreg.h"
33 #include "msi.h"
34 #include "msipriv.h"
35 #include "action.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40
41 typedef void (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *);
42
43 struct _events {
44     LPCSTR event;
45     EVENTHANDLER handler;
46 };
47
48 struct _subscriber {
49     LPWSTR control;
50     LPWSTR attribute;
51     struct _subscriber *next;
52 };
53
54 struct _subscription_chain {
55     LPWSTR event;
56     struct _subscriber *chain;
57 };
58
59 struct _subscriptions {
60     DWORD chain_count; 
61     struct _subscription_chain* chain;
62 };
63
64
65 VOID ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*);
66
67 /*
68  * Create a dialog box and run it if it's modal
69  */
70 static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name )
71 {
72     msi_dialog *dialog;
73     UINT r;
74
75     /* kill the current modeless dialog */
76     if( package->dialog )
77         msi_dialog_destroy( package->dialog );
78     package->dialog = NULL;
79
80     /* create a new dialog */
81     dialog = msi_dialog_create( package, name,
82                                 ControlEvent_HandleControlEvent );
83     if( dialog )
84     {
85         /* modeless dialogs return an error message */
86         r = msi_dialog_run_message_loop( dialog );
87         if( r == ERROR_SUCCESS )
88             msi_dialog_destroy( dialog );
89         else
90             package->dialog = dialog;
91     }
92     else
93         r = ERROR_FUNCTION_FAILED;
94
95     return r;
96 }
97
98
99 /*
100  * End a modal dialog box
101  */
102 static VOID ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, 
103                                    msi_dialog* dialog)
104 {
105     static const WCHAR szExit[] = {
106     'E','x','i','t',0};
107     static const WCHAR szRetry[] = {
108     'R','e','t','r','y',0};
109     static const WCHAR szIgnore[] = {
110     'I','g','n','o','r','e',0};
111     static const WCHAR szReturn[] = {
112     'R','e','t','u','r','n',0};
113
114     if (lstrcmpW(argument,szExit)==0)
115         package->CurrentInstallState = ERROR_INSTALL_USEREXIT;
116     else if (lstrcmpW(argument, szRetry) == 0)
117         package->CurrentInstallState = ERROR_INSTALL_SUSPEND;
118     else if (lstrcmpW(argument, szIgnore) == 0)
119         package->CurrentInstallState = -1;
120     else if (lstrcmpW(argument, szReturn) == 0)
121         package->CurrentInstallState = ERROR_SUCCESS;
122     else
123     {
124         ERR("Unknown argument string %s\n",debugstr_w(argument));
125         package->CurrentInstallState = ERROR_FUNCTION_FAILED;
126     }
127
128     msi_dialog_end_dialog( dialog );
129 }
130
131 /*
132  * transition from one modal dialog to another modal dialog
133  */
134 static VOID ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, 
135                                    msi_dialog *dialog)
136 {
137     /* store the name of the next dialog, and signal this one to end */
138     package->next_dialog = strdupW(argument);
139     msi_dialog_end_dialog( dialog );
140 }
141
142 /*
143  * Create a new child dialog of an existing modal dialog
144  */
145 static VOID ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, 
146                               msi_dialog *dialog)
147 {
148     event_do_dialog( package, argument );
149     if( package->CurrentInstallState != ERROR_SUCCESS )
150         msi_dialog_end_dialog( dialog );
151 }
152
153 /*
154  * Creates a dialog that remains up for a period of time
155  * based on a condition
156  */
157 static VOID ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, 
158                                   msi_dialog* dialog)
159 {
160     FIXME("Doing Nothing\n");
161 }
162
163 static VOID ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, 
164                                   msi_dialog* dialog)
165 {
166     ACTION_PerformAction(package,argument,TRUE);
167 }
168
169 static VOID ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument, 
170                                   msi_dialog* dialog)
171 {
172     static const WCHAR szAll[] = {'A','L','L',0};
173     int i;
174
175     if (lstrcmpW(szAll,argument))
176     {
177         MSI_SetFeatureStateW(package,argument,INSTALLSTATE_LOCAL);
178     }
179     else
180     {
181         for (i = 0; i < package->loaded_features; i++)
182         {
183             package->features[i].ActionRequest = INSTALLSTATE_LOCAL;
184             package->features[i].Action = INSTALLSTATE_LOCAL;
185         }
186         ACTION_UpdateComponentStates(package,argument);
187     }
188
189 }
190
191 static VOID ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument, 
192                                 msi_dialog* dialog)
193 {
194     static const WCHAR szAll[] = {'A','L','L',0};
195     int i;
196
197     if (lstrcmpW(szAll,argument))
198     {
199         MSI_SetFeatureStateW(package,argument,INSTALLSTATE_ABSENT);
200     }
201     else
202     {
203         for (i = 0; i < package->loaded_features; i++)
204         {
205             package->features[i].ActionRequest = INSTALLSTATE_ABSENT;
206             package->features[i].Action= INSTALLSTATE_ABSENT;
207         }
208         ACTION_UpdateComponentStates(package,argument);
209     }
210 }
211
212 static VOID ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument, 
213                                    msi_dialog* dialog)
214 {
215     static const WCHAR szAll[] = {'A','L','L',0};
216     int i;
217
218     if (lstrcmpW(szAll,argument))
219     {
220         MSI_SetFeatureStateW(package,argument,INSTALLSTATE_SOURCE);
221     }
222     else
223     {
224         for (i = 0; i < package->loaded_features; i++)
225         {
226             package->features[i].ActionRequest = INSTALLSTATE_SOURCE;
227             package->features[i].Action = INSTALLSTATE_SOURCE;
228         }
229         ACTION_UpdateComponentStates(package,argument);
230     }
231 }
232
233
234 /*
235  * Subscribed events
236  */
237 static void free_subscriber(struct _subscriber *who)
238 {
239     HeapFree(GetProcessHeap(),0,who->control);
240     HeapFree(GetProcessHeap(),0,who->attribute);
241     HeapFree(GetProcessHeap(),0,who);
242 }
243
244 VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, LPCWSTR event,
245                                    LPCWSTR control, LPCWSTR attribute)
246 {
247     int i;
248     struct _subscription_chain *chain;
249     struct _subscriber *subscriber, *ptr;
250
251     if (!package->EventSubscriptions)
252     {
253         package->EventSubscriptions = HeapAlloc(GetProcessHeap(), 0,
254                                        sizeof(struct _subscriptions));
255         package->EventSubscriptions->chain_count = 0;
256     }
257
258     chain = NULL;
259
260     for (i = 0; i < package->EventSubscriptions->chain_count; i++)
261     {
262         if (lstrcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
263         {
264             chain = &package->EventSubscriptions->chain[i];
265             break;
266         }
267     }
268
269     if (chain == NULL)
270     {
271         if (package->EventSubscriptions->chain_count)
272             chain = HeapReAlloc(GetProcessHeap(), 0,
273                                 package->EventSubscriptions->chain,
274                                (package->EventSubscriptions->chain_count + 1) *
275                                 sizeof (struct _subscription_chain)); 
276         else
277             chain= HeapAlloc(GetProcessHeap(),0, sizeof (struct 
278                                                         _subscription_chain));
279
280         package->EventSubscriptions->chain = chain;
281         chain = &package->EventSubscriptions->chain[
282                                 package->EventSubscriptions->chain_count];
283         package->EventSubscriptions->chain_count++;
284         memset(chain,0,sizeof(struct _subscription_chain));
285         chain->event = strdupW(event);
286     }
287
288     subscriber = ptr = chain->chain;
289     while (ptr)
290     {
291         subscriber = ptr;
292         ptr = ptr->next;
293     }
294
295     ptr = HeapAlloc(GetProcessHeap(),0,sizeof(struct _subscriber));
296     ptr->control = strdupW(control);
297     ptr->attribute = strdupW(attribute);
298     ptr->next = NULL;
299     
300     if (subscriber)
301         subscriber->next = ptr;
302     else
303         chain->chain = ptr;
304 }
305
306 VOID ControlEvent_UnSubscribeToEvent(MSIPACKAGE *package, LPCWSTR event,
307                                      LPCWSTR control, LPCWSTR attribute)
308 {
309     int i;
310
311     if (!package->EventSubscriptions)
312         return;
313
314     for (i = 0; i < package->EventSubscriptions->chain_count; i++)
315     {
316         if (lstrcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
317         {
318             struct _subscriber *who;
319             struct _subscriber *prev=NULL;
320             who = package->EventSubscriptions->chain[i].chain;
321             while (who)
322             {
323                 if (lstrcmpiW(who->control,control)==0
324                    && lstrcmpiW(who->attribute,attribute)==0)
325                 {
326                     if (prev)
327                         prev->next = who->next;
328                     else
329                         package->EventSubscriptions->chain[i].chain = who->next;
330         
331                     free_subscriber(who);
332                 }
333                 else
334                 {
335                     prev = who;
336                     who = who->next;
337                 }
338             }
339             break;
340         }
341     }
342 }
343
344 VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event, 
345                                       MSIRECORD *data)
346 {
347     int i;
348
349     TRACE("Firing Event %s\n",debugstr_w(event));
350
351     if (!package->dialog)
352         return;
353
354     if (!package->EventSubscriptions)
355         return;
356
357     for (i = 0; i < package->EventSubscriptions->chain_count; i++)
358     {
359         if (lstrcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
360         {
361             struct _subscriber *who;
362             who = package->EventSubscriptions->chain[i].chain;
363             while (who)
364             {
365                 ERR("Should Fire event for %s %s\n",
366                     debugstr_w(who->control), debugstr_w(who->attribute));
367                 /*
368                  msi_dialog_fire_subscribed_event(package->dialog, who->control,
369                                                   who->attribute, data);
370                 */
371                 who = who->next;
372             }
373             break;
374         }
375     }
376 }
377
378 VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package)
379 {
380     int i;
381
382     if (!package->EventSubscriptions)
383         return;
384
385     for (i = 0; i < package->EventSubscriptions->chain_count; i++)
386     {
387         struct _subscriber *who;
388         struct _subscriber *ptr;
389         who = package->EventSubscriptions->chain[i].chain;
390         while (who)
391         {
392             ptr = who;
393             who = who->next;
394             free_subscriber(ptr);
395         }
396         HeapFree(GetProcessHeap(), 0,
397                     package->EventSubscriptions->chain[i].event);
398     }
399     HeapFree(GetProcessHeap(),0,package->EventSubscriptions->chain);
400     HeapFree(GetProcessHeap(),0,package->EventSubscriptions);
401     package->EventSubscriptions = NULL;
402 }
403
404 /*
405  * ACTION_DialogBox()
406  *
407  * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
408  * if the given parameter is not a dialog box
409  */
410 UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName )
411 {
412     UINT r = ERROR_SUCCESS;
413
414     if( package->next_dialog )
415         ERR("Already a next dialog... ignoring it\n");
416     package->next_dialog = NULL;
417
418     /*
419      * Dialogs are chained by filling in the next_dialog member
420      *  of the package structure, then terminating the current dialog.
421      *  The code below sees the next_dialog member set, and runs the
422      *  next dialog.
423      * We fall out of the loop below if we come across a modeless
424      *  dialog, as it returns ERROR_IO_PENDING when we try to run
425      *  its message loop.
426      */
427     r = event_do_dialog( package, szDialogName );
428     while( r == ERROR_SUCCESS && package->next_dialog )
429     {
430         LPWSTR name = package->next_dialog;
431
432         package->next_dialog = NULL;
433         r = event_do_dialog( package, name );
434         HeapFree( GetProcessHeap(), 0, name );
435     }
436
437     if( r == ERROR_IO_PENDING )
438         r = ERROR_SUCCESS;
439
440     return r;
441 }
442
443 struct _events Events[] = {
444     { "EndDialog",ControlEvent_EndDialog },
445     { "NewDialog",ControlEvent_NewDialog },
446     { "SpawnDialog",ControlEvent_SpawnDialog },
447     { "SpawnWaitDialog",ControlEvent_SpawnWaitDialog },
448     { "DoAction",ControlEvent_DoAction },
449     { "AddLocal",ControlEvent_AddLocal },
450     { "Remove",ControlEvent_Remove },
451     { "AddSource",ControlEvent_AddSource },
452     { NULL,NULL },
453 };
454
455 VOID ControlEvent_HandleControlEvent(MSIPACKAGE *package, LPCWSTR event,
456                                      LPCWSTR argument, msi_dialog* dialog)
457 {
458     int i = 0;
459
460     TRACE("Handling Control Event %s\n",debugstr_w(event));
461     if (!event)
462         return;
463
464     while( Events[i].event != NULL)
465     {
466         LPWSTR wevent = strdupAtoW(Events[i].event);
467         if (lstrcmpW(wevent,event)==0)
468         {
469             HeapFree(GetProcessHeap(),0,wevent);
470             Events[i].handler(package,argument,dialog);
471             return;
472         }
473         HeapFree(GetProcessHeap(),0,wevent);
474         i++;
475     }
476     FIXME("unhandled control event %s arg(%s)\n",
477           debugstr_w(event), debugstr_w(argument));
478 }