PageRenderTime 66ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/dlls/msi/events.c

https://github.com/alama/wine
C | 449 lines | 328 code | 62 blank | 59 comment | 39 complexity | 1f1f7869c6b8a0219a27f4c75b971c0c MD5 | raw file
  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #include <stdarg.h>
  21. #include <stdio.h>
  22. #include "windef.h"
  23. #include "winbase.h"
  24. #include "winerror.h"
  25. #include "winreg.h"
  26. #include "msi.h"
  27. #include "msipriv.h"
  28. #include "wine/debug.h"
  29. #include "wine/unicode.h"
  30. WINE_DEFAULT_DEBUG_CHANNEL(msi);
  31. typedef UINT (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *);
  32. struct control_events
  33. {
  34. const WCHAR *event;
  35. EVENTHANDLER handler;
  36. };
  37. struct subscriber {
  38. struct list entry;
  39. msi_dialog *dialog;
  40. LPWSTR event;
  41. LPWSTR control;
  42. LPWSTR attribute;
  43. };
  44. static UINT ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*);
  45. /*
  46. * Create a dialog box and run it if it's modal
  47. */
  48. static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name, msi_dialog *parent, BOOL destroy_modeless )
  49. {
  50. msi_dialog *dialog;
  51. UINT r;
  52. /* create a new dialog */
  53. dialog = msi_dialog_create( package, name, parent,
  54. ControlEvent_HandleControlEvent );
  55. if( dialog )
  56. {
  57. /* kill the current modeless dialog */
  58. if( destroy_modeless && package->dialog )
  59. {
  60. msi_dialog_destroy( package->dialog );
  61. package->dialog = NULL;
  62. }
  63. /* modeless dialogs return an error message */
  64. r = msi_dialog_run_message_loop( dialog );
  65. if( r == ERROR_SUCCESS )
  66. msi_dialog_destroy( dialog );
  67. else
  68. package->dialog = dialog;
  69. }
  70. else
  71. r = ERROR_FUNCTION_FAILED;
  72. return r;
  73. }
  74. /*
  75. * End a modal dialog box
  76. */
  77. static UINT ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument,
  78. msi_dialog* dialog)
  79. {
  80. static const WCHAR szExit[] = {'E','x','i','t',0};
  81. static const WCHAR szRetry[] = {'R','e','t','r','y',0};
  82. static const WCHAR szIgnore[] = {'I','g','n','o','r','e',0};
  83. static const WCHAR szReturn[] = {'R','e','t','u','r','n',0};
  84. if (!strcmpW( argument, szExit ))
  85. package->CurrentInstallState = ERROR_INSTALL_USEREXIT;
  86. else if (!strcmpW( argument, szRetry ))
  87. package->CurrentInstallState = ERROR_INSTALL_SUSPEND;
  88. else if (!strcmpW( argument, szIgnore ))
  89. package->CurrentInstallState = ERROR_SUCCESS;
  90. else if (!strcmpW( argument, szReturn ))
  91. {
  92. msi_dialog *parent = msi_dialog_get_parent(dialog);
  93. msi_free(package->next_dialog);
  94. package->next_dialog = (parent) ? strdupW(msi_dialog_get_name(parent)) : NULL;
  95. package->CurrentInstallState = ERROR_SUCCESS;
  96. }
  97. else
  98. {
  99. ERR("Unknown argument string %s\n",debugstr_w(argument));
  100. package->CurrentInstallState = ERROR_FUNCTION_FAILED;
  101. }
  102. ControlEvent_CleanupDialogSubscriptions(package, msi_dialog_get_name( dialog ));
  103. msi_dialog_end_dialog( dialog );
  104. return ERROR_SUCCESS;
  105. }
  106. /*
  107. * transition from one modal dialog to another modal dialog
  108. */
  109. static UINT ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument,
  110. msi_dialog *dialog)
  111. {
  112. /* store the name of the next dialog, and signal this one to end */
  113. package->next_dialog = strdupW(argument);
  114. ControlEvent_CleanupSubscriptions(package);
  115. msi_dialog_end_dialog( dialog );
  116. return ERROR_SUCCESS;
  117. }
  118. /*
  119. * Create a new child dialog of an existing modal dialog
  120. */
  121. static UINT ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument,
  122. msi_dialog *dialog)
  123. {
  124. /* don't destroy a modeless dialogs that might be our parent */
  125. event_do_dialog( package, argument, dialog, FALSE );
  126. if( package->CurrentInstallState != ERROR_SUCCESS )
  127. msi_dialog_end_dialog( dialog );
  128. return ERROR_SUCCESS;
  129. }
  130. /*
  131. * Creates a dialog that remains up for a period of time
  132. * based on a condition
  133. */
  134. static UINT ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument,
  135. msi_dialog* dialog)
  136. {
  137. FIXME("Doing Nothing\n");
  138. return ERROR_SUCCESS;
  139. }
  140. static UINT ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument,
  141. msi_dialog* dialog)
  142. {
  143. ACTION_PerformAction(package, argument, SCRIPT_NONE);
  144. return ERROR_SUCCESS;
  145. }
  146. static UINT ControlEvent_AddLocal( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog )
  147. {
  148. MSIFEATURE *feature;
  149. LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
  150. {
  151. if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll ))
  152. {
  153. if (feature->ActionRequest != INSTALLSTATE_LOCAL)
  154. msi_set_property( package->db, szPreselected, szOne );
  155. MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_LOCAL );
  156. }
  157. }
  158. return ERROR_SUCCESS;
  159. }
  160. static UINT ControlEvent_Remove( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog )
  161. {
  162. MSIFEATURE *feature;
  163. LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
  164. {
  165. if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll ))
  166. {
  167. if (feature->ActionRequest != INSTALLSTATE_ABSENT)
  168. msi_set_property( package->db, szPreselected, szOne );
  169. MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_ABSENT );
  170. }
  171. }
  172. return ERROR_SUCCESS;
  173. }
  174. static UINT ControlEvent_AddSource( MSIPACKAGE *package, LPCWSTR argument, msi_dialog *dialog )
  175. {
  176. MSIFEATURE *feature;
  177. LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
  178. {
  179. if (!strcmpW( argument, feature->Feature ) || !strcmpW( argument, szAll ))
  180. {
  181. if (feature->ActionRequest != INSTALLSTATE_SOURCE)
  182. msi_set_property( package->db, szPreselected, szOne );
  183. MSI_SetFeatureStateW( package, feature->Feature, INSTALLSTATE_SOURCE );
  184. }
  185. }
  186. return ERROR_SUCCESS;
  187. }
  188. static UINT ControlEvent_SetTargetPath(MSIPACKAGE* package, LPCWSTR argument,
  189. msi_dialog* dialog)
  190. {
  191. static const WCHAR szSelectionPath[] = {'S','e','l','e','c','t','i','o','n','P','a','t','h',0};
  192. LPWSTR path = msi_dup_property( package->db, argument );
  193. MSIRECORD *rec = MSI_CreateRecord( 1 );
  194. UINT r = ERROR_SUCCESS;
  195. MSI_RecordSetStringW( rec, 1, path );
  196. ControlEvent_FireSubscribedEvent( package, szSelectionPath, rec );
  197. if (path)
  198. {
  199. /* failure to set the path halts the executing of control events */
  200. r = MSI_SetTargetPathW(package, argument, path);
  201. msi_free(path);
  202. }
  203. msi_free(&rec->hdr);
  204. return r;
  205. }
  206. static UINT ControlEvent_Reset(MSIPACKAGE* package, LPCWSTR argument,
  207. msi_dialog* dialog)
  208. {
  209. msi_dialog_reset(dialog);
  210. return ERROR_SUCCESS;
  211. }
  212. /*
  213. * Subscribed events
  214. */
  215. static void free_subscriber( struct subscriber *sub )
  216. {
  217. msi_free(sub->event);
  218. msi_free(sub->control);
  219. msi_free(sub->attribute);
  220. msi_free(sub);
  221. }
  222. VOID ControlEvent_SubscribeToEvent( MSIPACKAGE *package, msi_dialog *dialog,
  223. LPCWSTR event, LPCWSTR control, LPCWSTR attribute )
  224. {
  225. struct subscriber *sub;
  226. TRACE("event %s control %s attribute %s\n", debugstr_w(event), debugstr_w(control), debugstr_w(attribute));
  227. LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry )
  228. {
  229. if (!strcmpiW( sub->event, event ) &&
  230. !strcmpiW( sub->control, control ) &&
  231. !strcmpiW( sub->attribute, attribute ))
  232. {
  233. TRACE("already subscribed\n");
  234. return;
  235. };
  236. }
  237. if (!(sub = msi_alloc( sizeof(*sub) ))) return;
  238. sub->dialog = dialog;
  239. sub->event = strdupW(event);
  240. sub->control = strdupW(control);
  241. sub->attribute = strdupW(attribute);
  242. list_add_tail( &package->subscriptions, &sub->entry );
  243. }
  244. VOID ControlEvent_FireSubscribedEvent( MSIPACKAGE *package, LPCWSTR event, MSIRECORD *rec )
  245. {
  246. struct subscriber *sub;
  247. TRACE("Firing event %s\n", debugstr_w(event));
  248. LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry )
  249. {
  250. if (strcmpiW( sub->event, event )) continue;
  251. msi_dialog_handle_event( sub->dialog, sub->control, sub->attribute, rec );
  252. }
  253. }
  254. VOID ControlEvent_CleanupDialogSubscriptions(MSIPACKAGE *package, LPWSTR dialog)
  255. {
  256. struct list *i, *t;
  257. struct subscriber *sub;
  258. LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
  259. {
  260. sub = LIST_ENTRY( i, struct subscriber, entry );
  261. if (strcmpW( msi_dialog_get_name( sub->dialog ), dialog ))
  262. continue;
  263. list_remove( &sub->entry );
  264. free_subscriber( sub );
  265. }
  266. }
  267. VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package)
  268. {
  269. struct list *i, *t;
  270. struct subscriber *sub;
  271. LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
  272. {
  273. sub = LIST_ENTRY( i, struct subscriber, entry );
  274. list_remove( &sub->entry );
  275. free_subscriber( sub );
  276. }
  277. }
  278. /*
  279. * ACTION_DialogBox()
  280. *
  281. * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
  282. * if the given parameter is not a dialog box
  283. */
  284. UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName )
  285. {
  286. UINT r = ERROR_SUCCESS;
  287. if( package->next_dialog )
  288. ERR("Already a next dialog... ignoring it\n");
  289. package->next_dialog = NULL;
  290. /*
  291. * Dialogs are chained by filling in the next_dialog member
  292. * of the package structure, then terminating the current dialog.
  293. * The code below sees the next_dialog member set, and runs the
  294. * next dialog.
  295. * We fall out of the loop below if we come across a modeless
  296. * dialog, as it returns ERROR_IO_PENDING when we try to run
  297. * its message loop.
  298. */
  299. r = event_do_dialog( package, szDialogName, NULL, TRUE );
  300. while( r == ERROR_SUCCESS && package->next_dialog )
  301. {
  302. LPWSTR name = package->next_dialog;
  303. package->next_dialog = NULL;
  304. r = event_do_dialog( package, name, NULL, TRUE );
  305. msi_free( name );
  306. }
  307. if( r == ERROR_IO_PENDING )
  308. r = ERROR_SUCCESS;
  309. return r;
  310. }
  311. static UINT ControlEvent_SetInstallLevel(MSIPACKAGE* package, LPCWSTR argument,
  312. msi_dialog* dialog)
  313. {
  314. int iInstallLevel = atolW(argument);
  315. TRACE("Setting install level: %i\n", iInstallLevel);
  316. return MSI_SetInstallLevel( package, iInstallLevel );
  317. }
  318. static UINT ControlEvent_DirectoryListUp(MSIPACKAGE *package, LPCWSTR argument,
  319. msi_dialog *dialog)
  320. {
  321. return msi_dialog_directorylist_up( dialog );
  322. }
  323. static UINT ControlEvent_ReinstallMode(MSIPACKAGE *package, LPCWSTR argument,
  324. msi_dialog *dialog)
  325. {
  326. return msi_set_property( package->db, szReinstallMode, argument );
  327. }
  328. static UINT ControlEvent_Reinstall( MSIPACKAGE *package, LPCWSTR argument,
  329. msi_dialog *dialog )
  330. {
  331. return msi_set_property( package->db, szReinstall, argument );
  332. }
  333. static UINT ControlEvent_ValidateProductID(MSIPACKAGE *package, LPCWSTR argument,
  334. msi_dialog *dialog)
  335. {
  336. return msi_validate_product_id( package );
  337. }
  338. static const WCHAR end_dialogW[] = {'E','n','d','D','i','a','l','o','g',0};
  339. static const WCHAR new_dialogW[] = {'N','e','w','D','i','a','l','o','g',0};
  340. static const WCHAR spawn_dialogW[] = {'S','p','a','w','n','D','i','a','l','o','g',0};
  341. static const WCHAR spawn_wait_dialogW[] = {'S','p','a','w','n','W','a','i','t','D','i','a','l','o','g',0};
  342. static const WCHAR do_actionW[] = {'D','o','A','c','t','i','o','n',0};
  343. static const WCHAR add_localW[] = {'A','d','d','L','o','c','a','l',0};
  344. static const WCHAR removeW[] = {'R','e','m','o','v','e',0};
  345. static const WCHAR add_sourceW[] = {'A','d','d','S','o','u','r','c','e',0};
  346. static const WCHAR set_target_pathW[] = {'S','e','t','T','a','r','g','e','t','P','a','t','h',0};
  347. static const WCHAR resetW[] = {'R','e','s','e','t',0};
  348. static const WCHAR set_install_levelW[] = {'S','e','t','I','n','s','t','a','l','l','L','e','v','e','l',0};
  349. static const WCHAR directory_list_upW[] = {'D','i','r','e','c','t','o','r','y','L','i','s','t','U','p',0};
  350. static const WCHAR selection_browseW[] = {'S','e','l','e','c','t','i','o','n','B','r','o','w','s','e',0};
  351. static const WCHAR reinstall_modeW[] = {'R','e','i','n','s','t','a','l','l','M','o','d','e',0};
  352. static const WCHAR reinstallW[] = {'R','e','i','n','s','t','a','l','l',0};
  353. static const WCHAR validate_product_idW[] = {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
  354. static const struct control_events control_events[] =
  355. {
  356. { end_dialogW, ControlEvent_EndDialog },
  357. { new_dialogW, ControlEvent_NewDialog },
  358. { spawn_dialogW, ControlEvent_SpawnDialog },
  359. { spawn_wait_dialogW, ControlEvent_SpawnWaitDialog },
  360. { do_actionW, ControlEvent_DoAction },
  361. { add_localW, ControlEvent_AddLocal },
  362. { removeW, ControlEvent_Remove },
  363. { add_sourceW, ControlEvent_AddSource },
  364. { set_target_pathW, ControlEvent_SetTargetPath },
  365. { resetW, ControlEvent_Reset },
  366. { set_install_levelW, ControlEvent_SetInstallLevel },
  367. { directory_list_upW, ControlEvent_DirectoryListUp },
  368. { selection_browseW, ControlEvent_SpawnDialog },
  369. { reinstall_modeW, ControlEvent_ReinstallMode },
  370. { reinstallW, ControlEvent_Reinstall },
  371. { validate_product_idW, ControlEvent_ValidateProductID },
  372. { NULL, NULL }
  373. };
  374. UINT ControlEvent_HandleControlEvent( MSIPACKAGE *package, LPCWSTR event,
  375. LPCWSTR argument, msi_dialog *dialog )
  376. {
  377. unsigned int i;
  378. TRACE("handling control event %s\n", debugstr_w(event));
  379. if (!event) return ERROR_SUCCESS;
  380. for (i = 0; control_events[i].event; i++)
  381. {
  382. if (!strcmpW( control_events[i].event, event ))
  383. return control_events[i].handler( package, argument, dialog );
  384. }
  385. FIXME("unhandled control event %s arg(%s)\n", debugstr_w(event), debugstr_w(argument));
  386. return ERROR_SUCCESS;
  387. }